<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#222"><meta name="generator" content="Hexo 5.4.2">

  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">



<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" integrity="sha256-HtsXJanqjKTc8vVQjO4YMhiqFoXkfBsjBWcX91T1jr8=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.1.1/animate.min.css" integrity="sha256-PR7ttpcvz8qrF57fur/yAx1qXMFJeJFiA6pSzWi0OIE=" crossorigin="anonymous">

<script class="next-config" data-name="main" type="application/json">{"hostname":"xiaoqingming18.github.io","root":"/","images":"/images","scheme":"Muse","darkmode":false,"version":"8.15.1","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":{"enable":false,"style":null},"bookmark":{"enable":false,"color":"#222","save":"auto"},"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"stickytabs":false,"motion":{"enable":true,"async":false,"transition":{"menu_item":"fadeInDown","post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果：${query}","hits_time":"找到 ${hits} 个搜索结果（用时 ${time} 毫秒）","hits":"找到 ${hits} 个搜索结果"}}</script><script src="/js/config.js"></script>

    <meta name="description" content="记录我的学习、生活和恋爱日常">
<meta property="og:type" content="website">
<meta property="og:title" content="鸣蜩十七">
<meta property="og:url" content="http://xiaoqingming18.github.io/page/2/index.html">
<meta property="og:site_name" content="鸣蜩十七">
<meta property="og:description" content="记录我的学习、生活和恋爱日常">
<meta property="og:locale" content="zh_CN">
<meta property="article:author" content="鸣蜩十七">
<meta name="twitter:card" content="summary">


<link rel="canonical" href="http://xiaoqingming18.github.io/page/2/">



<script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":true,"isPost":false,"lang":"zh-CN","comments":"","permalink":"","path":"page/2/index.html","title":""}</script>

<script class="next-config" data-name="calendar" type="application/json">""</script>
<title>鸣蜩十七</title>
  








  <noscript>
    <link rel="stylesheet" href="/css/noscript.css">
  </noscript>

<style>.github-emoji { position: relative; display: inline-block; width: 1.2em; min-height: 1.2em; overflow: hidden; vertical-align: top; color: transparent; }  .github-emoji > span { position: relative; z-index: 10; }  .github-emoji img, .github-emoji .fancybox { margin: 0 !important; padding: 0 !important; border: none !important; outline: none !important; text-decoration: none !important; user-select: none !important; cursor: auto !important; }  .github-emoji img { height: 1.2em !important; width: 1.2em !important; position: absolute !important; left: 50% !important; top: 50% !important; transform: translate(-50%, -50%) !important; user-select: none !important; cursor: auto !important; } .github-emoji-fallback { color: inherit; } .github-emoji-fallback img { opacity: 0 !important; }</style>
</head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <div class="column">
      <header class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏" role="button">
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <i class="logo-line"></i>
      <h1 class="site-title">鸣蜩十七</h1>
      <i class="logo-line"></i>
    </a>
      <p class="site-subtitle" itemprop="description">鸣蜩十七是和女朋友谈恋爱的日子</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger" aria-label="搜索" role="button">
    </div>
  </div>
</div>



<nav class="site-nav">
  <ul class="main-menu menu"><li class="menu-item menu-item-home"><a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a></li><li class="menu-item menu-item-about"><a href="/about/" rel="section"><i class="fa fa-user fa-fw"></i>关于</a></li><li class="menu-item menu-item-tags"><a href="/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签</a></li><li class="menu-item menu-item-categories"><a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a></li><li class="menu-item menu-item-archives"><a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a></li><li class="menu-item menu-item-schedule"><a href="/schedule/" rel="section"><i class="fa fa-calendar fa-fw"></i>日程表</a></li><li class="menu-item menu-item-sitemap"><a href="/sitemap.xml" rel="section"><i class="fa fa-sitemap fa-fw"></i>站点地图</a></li><li class="menu-item menu-item-commonweal"><a href="/404/" rel="section"><i class="fa fa-heartbeat fa-fw"></i>公益 404</a></li>
  </ul>
</nav>




</header>
        
  
  <aside class="sidebar">

    <div class="sidebar-inner sidebar-overview-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">鸣蜩十七</p>
  <div class="site-description" itemprop="description">记录我的学习、生活和恋爱日常</div>
</div>
<div class="site-state-wrap animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
        <a href="/archives/">
          <span class="site-state-item-count">43</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
          <a href="/categories/">
        <span class="site-state-item-count">8</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
          <a href="/tags/">
        <span class="site-state-item-count">37</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>

        </div>
      </div>
    </div>

    
  </aside>


    </div>

    <div class="main-inner index posts-expand">

    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://xiaoqingming18.github.io/2023/06/13/Java%EF%BC%9A%E5%A4%9A%E7%BA%BF%E7%A8%8B/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="鸣蜩十七">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="鸣蜩十七">
      <meta itemprop="description" content="记录我的学习、生活和恋爱日常">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 鸣蜩十七">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2023/06/13/Java%EF%BC%9A%E5%A4%9A%E7%BA%BF%E7%A8%8B/" class="post-title-link" itemprop="url">Java：多线程</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2023-06-13 21:24:53" itemprop="dateCreated datePublished" datetime="2023-06-13T21:24:53+08:00">2023-06-13</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-06-27 20:23:36" itemprop="dateModified" datetime="2023-06-27T20:23:36+08:00">2023-06-27</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%90%8E%E7%AB%AF/" itemprop="url" rel="index"><span itemprop="name">-后端</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="程序、进程与线程"><a href="#程序、进程与线程" class="headerlink" title="程序、进程与线程"></a>程序、进程与线程</h1><p>程序、进程和线程是计算机中不同概念，它们在执行和管理任务的过程中扮演着不同的角色。</p>
<ol>
<li>程序（Program）：程序是指一组按照特定顺序组织的指令，用于告诉计算机如何执行某种任务。程序通常以源代码的形式编写，通过编译或解释等过程转换为机器可以理解和执行的形式。</li>
<li>进程（Process）：进程是一个正在执行的程序的实例。当程序被加载到内存中并执行时，操作系统会为该程序创建一个进程。每个进程都有自己的内存空间、寄存器集合、文件和资源。进程是计算机中任务执行和资源管理的基本单位。</li>
<li>线程（Thread）：线程是进程中的一个执行路径。一个进程可以包含多个线程，每个线程都可以独立地执行特定的任务。线程共享进程的内存空间和资源，可以更高效地完成并发执行的任务。线程之间可以共享数据和通信，但也需要合适的同步机制来保证数据的一致性和避免竞态条件。</li>
</ol>
<p>总的来说，程序是一组指令的集合，进程是程序在执行时的实例，而线程是进程内部的执行路径，用于并发执行任务。进程提供了资源分配和管理的环境，而线程提供了更细粒度的并发控制和共享资源的机制。</p>
<h1 id="创建线程的方式"><a href="#创建线程的方式" class="headerlink" title="创建线程的方式"></a>创建线程的方式</h1><p>在Java中，创建线程有四种方式：</p>
<ol>
<li>使用<code>Thread</code>类的子类：Java提供了<code>Thread</code>类，可以通过继承<code>Thread</code>类并重写<code>run()</code>方法来创建线程。</li>
<li>使用<code>Runnable</code>接口：Java中的线程类可以实现<code>Runnable</code>接口，并在<code>run()</code>方法中定义线程的逻辑。这种方式更加灵活，可以避免类的单继承限制。</li>
<li>使用匿名类：可以使用匿名类的方式直接创建线程，并重写<code>run()</code>方法。</li>
<li>使用<code>Lambda</code>表达式：在Java 8及以上版本中，可以使用<code>Lambda</code>表达式来创建线程。</li>
</ol>
<p>重点掌握前两种即可，下面详细介绍一下各种方式创建线程。</p>
<h2 id="使用Thread类的子类创建线程"><a href="#使用Thread类的子类创建线程" class="headerlink" title="使用Thread类的子类创建线程"></a>使用<code>Thread</code>类的子类创建线程</h2><p>在Java中，可以使用<code>Thread</code>类来创建和管理线程，下面是使用<code>Thread</code>类创建线程的一般步骤。</p>
<p>首先，创建一个继承自Thread类的子类。在子类中重写Thread类的run()方法，该方法包含了线程要执行的代码。</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MyThread</span> <span class="keyword">extends</span> <span class="title class_">Thread</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> {</span><br><span class="line">        <span class="comment">// 线程执行的代码</span></span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>接着需要创建线程对象。在主程序或其他类中，实例化Thread子类的对象。</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">MyThread</span> <span class="variable">thread</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MyThread</span>();</span><br></pre></td></tr></tbody></table></figure>
<p>最后启动线程，调用线程对象的<code>start()</code>方法，以启动线程并开始执行<code>run()</code>方法中的代码。</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">thread.start();</span><br></pre></td></tr></tbody></table></figure>
<h2 id="使用Runnable接口创建线程"><a href="#使用Runnable接口创建线程" class="headerlink" title="使用Runnable接口创建线程"></a>使用<code>Runnable</code>接口创建线程</h2><p>相比于继承<code>Thread</code>类，实现<code>Runnable</code>接口有以下优势：</p>
<ol>
<li>避免类的单继承限制：Java中一个类只能继承一个父类，但是可以实现多个接口。使用<code>Runnable</code>接口可以在不受单继承限制的情况下创建线程。</li>
<li>提高代码的可重用性：通过实现<code>Runnable</code>接口，将任务逻辑与线程类的定义分离开来，使得任务可以在不同的线程中重复使用。</li>
</ol>
<p>下面是使用<code>Runnable</code>接口创建线程的一般步骤。</p>
<p>首先，创建一个实现<code>Runnable</code>接口的类，并实现<code>run()方法</code>，该方法包含了线程要执行的代码。</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MyRunnable</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> {</span><br><span class="line">        <span class="comment">// 线程执行的代码</span></span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p> 接着创建一个线程对象，并将<code>Runnable</code>对象作为参数传递给<code>Thread</code>类的构造函数。</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Runnable</span> <span class="variable">myRunnable</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Runnable</span>();</span><br><span class="line"><span class="type">Thread</span> <span class="variable">thread</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(myRunnable);</span><br></pre></td></tr></tbody></table></figure>
<p>最后，启动线程。调用线程对象的<code>start()</code>方法，以启动线程并开始执行<code>run()</code>方法中的代码。</p>
<h2 id="使用匿名类创建线程"><a href="#使用匿名类创建线程" class="headerlink" title="使用匿名类创建线程"></a>使用匿名类创建线程</h2><p>下面是一个使用匿名类创建线程的示例：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MainClass</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">        <span class="comment">// 使用匿名类创建线程</span></span><br><span class="line">        <span class="type">Thread</span> <span class="variable">thread</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(<span class="keyword">new</span> <span class="title class_">Runnable</span>() {</span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> {</span><br><span class="line">                <span class="comment">// 线程执行的代码</span></span><br><span class="line">            }</span><br><span class="line">        });</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 启动线程</span></span><br><span class="line">        thread.start();</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<h2 id="使用Lambda表达式创建线程"><a href="#使用Lambda表达式创建线程" class="headerlink" title="使用Lambda表达式创建线程"></a>使用<code>Lambda</code>表达式创建线程</h2><p>下面是一个使用lambda表达式创建线程的示例：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">        <span class="comment">// 使用lambda表达式创建线程</span></span><br><span class="line">        <span class="type">Thread</span> <span class="variable">thread</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; {</span><br><span class="line">            <span class="comment">// 线程的逻辑代码</span></span><br><span class="line">            System.out.println(<span class="string">"线程正在运行"</span>);</span><br><span class="line">        });</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 启动线程</span></span><br><span class="line">        thread.start();</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<h1 id="与线程相关的方法"><a href="#与线程相关的方法" class="headerlink" title="与线程相关的方法"></a>与线程相关的方法</h1><h2 id="获取当前线程"><a href="#获取当前线程" class="headerlink" title="获取当前线程"></a>获取当前线程</h2><p>可以通过调用静态方法<code>Thread.currentThread()</code>获取当前线程。</p>
<h1 id="线程的状态"><a href="#线程的状态" class="headerlink" title="线程的状态"></a>线程的状态</h1><p>在Java中，线程可以有下面6种状态：</p>
<ol>
<li><strong>New（新建）</strong>：当线程对象被创建时，它处于新建状态。此时，线程还没有启动，尚未调用<code>start</code>方法。</li>
<li><strong>Runnable（可运行）</strong>：当线程启动后，它进入可运行状态。在可运行状态下，线程可能正在执行，也可能等待操作系统的调度以获得执行时间。</li>
<li><strong>Blocked（阻塞）</strong>：一个线程可能被阻塞，无法继续执行。当线程在等待某个监视器锁时，它会进入阻塞状态。例如，当一个线程调用了一个同步方法，而该方法已被其他线程占用时，该线程将被阻塞。</li>
<li><strong>Waiting（等待）</strong>：线程在等待某个特定条件发生时，可能进入等待状态。一个线程可以通过调用<code>Object</code>类的<code>wait</code>方法或线程的<code>join</code>方法进入等待状态。</li>
<li><strong>Timed Waiting（计时等待）</strong>：线程在等待一段指定的时间时，可能进入计时等待状态。一个线程可以通过调用<code>Thread</code>类的<code>sleep</code>方法、<code>Object</code>类的<code>wait</code>方法（带超时参数）或线程的<code>join</code>方法（带超时参数）进入计时等待状态。</li>
<li><strong>Terminated（终止）</strong>：线程的生命周期结束，处于终止状态。线程可能因为执行完毕、抛出异常或被中断而终止。</li>
</ol>
<p>下面详细介绍一下这几种状态。</p>
<h2 id="新建线程"><a href="#新建线程" class="headerlink" title="新建线程"></a>新建线程</h2><p>当新建一个线程，但是还没有调用该线程的<code>start()</code>方法，此时该线程的状态就是<strong>新建（new）</strong>。</p>
<h2 id="可运行线程"><a href="#可运行线程" class="headerlink" title="可运行线程"></a>可运行线程</h2><p>一旦调用<code>start()</code>方法，线程就处于<strong>可运行（Runnable）</strong>状态。一个可运行状态可能正在运行也可能没有运行。要由操作系统为线程提供具体的运行时间。</p>
<p>一个线程开始运行，它不一定始终保持运行。运行中的线程有时需要暂停，让其他线程有机会运行。</p>
<h2 id="阻塞和等待线程"><a href="#阻塞和等待线程" class="headerlink" title="阻塞和等待线程"></a>阻塞和等待线程</h2><p>当线程处于阻塞和等待状态时，它暂时不活动，不运行任何代码。</p>
<p><strong>==此处等待补充==</strong></p>
<h2 id="终止线程"><a href="#终止线程" class="headerlink" title="终止线程"></a>终止线程</h2><p>线程会由于以下两个原因之一而终止：</p>
<ul>
<li><code>run()</code>方法正常退出，线程自然终止。</li>
<li>因为一个没有捕获的异常终止了<code>run()</code>方法，使线程意外终止。</li>
</ul>
<h1 id="线程属性"><a href="#线程属性" class="headerlink" title="线程属性"></a>线程属性</h1><h2 id="中断线程"><a href="#中断线程" class="headerlink" title="中断线程"></a>中断线程</h2><p>原先有一个<code>stop()</code>方法可以强制终止一个线程，但现在这个方法已经废弃了，除了这个废弃的方法，没有办法可以<strong>强制终止</strong>一个线程。线程只能通过上一小节说的两个原因其中之一，让线程自然终止。</p>
<p>不过，<code>interrupt()</code>方法可以用来<strong>请求</strong>终止一个线程。当对一个线程调用该方法时，就会设置线程的<strong>中断状态</strong>。这是每个线程都有的<code>bollean</code>标志。每个线程都应该时不时地检查这个标志，以判断线程是否被中断。</p>
<p>可以通过<code>isInterrupted()</code>方法得出当前线程是否设置了中断状态：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span>(!Thread.currentThread().isInterrupted()) {</span><br><span class="line">    <span class="comment">// 要执行的代码</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>要注意的是，<strong>如果线程被阻塞就无法检查中断状态</strong>。这里就要引入<code>InterruptedException</code>异常。当在一个被<code>sleep()</code>或<code>wait()</code>调用阻塞的线程上调用<code>interrupt()</code>方法时，阻塞调用（即<code>sleep()</code>或<code>wait()</code>）将被一个<code>InterruptedException</code>异常中断，并进入<code>Runnable</code>状态，重新排队等待CPU资源。</p>
<p>例如，假设一个线程在执行<code>Thread.sleep(5000)</code>时被阻塞，即线程将休眠5秒钟。如果我们在另一个线程中调用阻塞线程的<code>interrupt()</code>方法，阻塞线程会立即抛出<code>InterruptedException</code>异常，而不是继续等待5秒钟，并且继续排队等待CPU资源。</p>
<h3 id="将中断解释为终止"><a href="#将中断解释为终止" class="headerlink" title="将中断解释为终止"></a>将中断解释为终止</h3><p>我们通常希望在使用<code>interrupt()</code>终止一个线程，而不是设置该线程的中断状态，那么可以像下面这样写<code>run()</code>方法：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Runnable</span> <span class="variable">r</span> <span class="operator">=</span> () -&gt; {</span><br><span class="line">    <span class="keyword">try</span> {</span><br><span class="line">        <span class="keyword">while</span>(!Thread.currentThread().isInterrupted()) {</span><br><span class="line">            <span class="comment">// 执行代码</span></span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">catch</span>(InterruptedException w) {</span><br><span class="line">        <span class="comment">// 线程被sleep或wait阻塞，无法检查中断状态</span></span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>在上面的<code>run()</code>方法中，在执行<code>try</code>块内的代码时需要先检查线程的中断状态，如果设置了中断状态，就不执行操作，线程自然终止。如果该线程被<code>sleep()</code>或<code>wait()</code>阻塞，就无法检查中断状态，也就无法达到通过设置中断来终止线程的效果。</p>
<h3 id="调用sleep就不要检测中断状态"><a href="#调用sleep就不要检测中断状态" class="headerlink" title="调用sleep就不要检测中断状态"></a>调用sleep就不要检测中断状态</h3><p>调用了<code>sleep()</code>方法之后（或者其他可中断方法），<code>isInterrupted()</code>检查既没必要也没有用处。</p>
<p>因为设置了中断状态后，调用<code>sleep()</code>方法并不会休眠，而是会清除中断状态并抛出<code>InterruptedException</code>异常。</p>
<h2 id="线程名"><a href="#线程名" class="headerlink" title="线程名"></a>线程名</h2><p>可以用<code>setName()</code>方法为线程设置任何名字：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">var</span> <span class="variable">t</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(runnable);</span><br><span class="line">t.setName(<span class="string">"ThreadName"</span>);</span><br></pre></td></tr></tbody></table></figure>
<h2 id="未捕获异常的处理器"><a href="#未捕获异常的处理器" class="headerlink" title="未捕获异常的处理器"></a>未捕获异常的处理器</h2><p>线程的<code>run()</code>方法不能抛出任何检查型异常，但是，非检查型异常可能会导致线程终止，这会导致线程死亡。</p>
<p>实际上，在线程死亡之前，异常会传递到一个用于处理未捕获异常的处理器。</p>
<p>这个处理器必须属于一个实现了<code>Thread.UncaughtExceptionHandler</code>接口的类。这个接口只有一个方法：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> <span class="title function_">uncaughtException</span><span class="params">(Thread t,Throwable e)</span></span><br></pre></td></tr></tbody></table></figure>
<h1 id="同步"><a href="#同步" class="headerlink" title="同步"></a>同步</h1><p>大多数多线程应用中，两个或两个以上线程需要共享对同一数据的存取。如果两个线程同时存取同一对象，并且每个线程分别调用了一个修改该对象状态的方法，这两个线程会相互覆盖，可能会导致对象的破坏。这种情况称为<strong>静态条件</strong>。</p>
<p>为了避免多线程破坏共享数据，必须学会如何<strong>同步存取</strong>。</p>
<h2 id="锁对象"><a href="#锁对象" class="headerlink" title="锁对象"></a>锁对象</h2><h3 id="锁的使用结构"><a href="#锁的使用结构" class="headerlink" title="锁的使用结构"></a>锁的使用结构</h3><p>Java5提供了一个<code>ReentrantLock</code>类来防止并发访问代码块，用<code>ReentrantLock</code>保护代码块的基本结构如下：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">myLock.lock();</span><br><span class="line"><span class="keyword">try</span> {</span><br><span class="line">    </span><br><span class="line">}</span><br><span class="line"><span class="keyword">finally</span> {</span><br><span class="line">    myLock.unlock();</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>这个锁是在要被访问的代码块中的，而不是放在线程中。</p>
<h3 id="锁的使用举例"><a href="#锁的使用举例" class="headerlink" title="锁的使用举例"></a>锁的使用举例</h3><p>下面来看一个使用锁来保护<code>Bank</code>类的<code>transfer()</code>方法：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Bank</span> {</span><br><span class="line">    <span class="keyword">private</span> <span class="type">var</span> <span class="variable">bankLock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantLock</span>();</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">transfer</span><span class="params">(<span class="type">int</span> form,<span class="type">int</span> to,<span class="type">int</span> amount)</span> {</span><br><span class="line">        bankLock.lock();</span><br><span class="line">        <span class="keyword">try</span> {</span><br><span class="line">            System.out.println(Thread.currentThread());</span><br><span class="line">            accounts[form] - = amount;</span><br><span class="line">            accounts[to] += amount;</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">finally</span> {</span><br><span class="line">            bankLock.unlock();</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>假设一个线程调用了<code>transfer()</code>，但是在执行结束前被强占。再假设第二个线程也调用<code>transfer()</code>方法，由于第二个线程不能获得锁，将在调用<code>lock()</code>方法时被阻塞。它会暂停，必须等待第一个线程执行完<code>transfer()</code>方法。当第一个线程释放锁时，第二个线程才能开始运行。</p>
<h3 id="锁的嵌套调用与持有计数"><a href="#锁的嵌套调用与持有计数" class="headerlink" title="锁的嵌套调用与持有计数"></a>锁的嵌套调用与持有计数</h3><p>这个锁称为重入（reentrant）锁，因为线程可以反复获得已拥有的锁。锁有一个<strong>持有计数（hold count）</strong>来跟踪对<code>lock()</code>方法的嵌套调用。例如，<code>transfer()</code>方法调用<code>getTotalBalance()</code>方法，这个方法也会封锁<code>bankLock</code>对象，此时<code>bankLock</code>对象的持有计数为2，当<code>getTotalBalance()</code>方法退出时，持有计数变回1。当<code>transfer()</code>方法退出时，持有计数变为0，线程释放锁。</p>
<h3 id="条件对象"><a href="#条件对象" class="headerlink" title="条件对象"></a>条件对象</h3><p>通常，线程进入临界区后却发现只有满足某个条件之后它才能执行。可以使用一个条件对象来管理那些已经获得了一个锁却不能做有用工作的线程。</p>
<p>继续来看一个<code>Bank</code>类存在的问题，如果一个账户没有足够的资金转账，一般使用的是下面这样的判断逻辑:</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(bank.getBalance(from) &gt;= amount) {</span><br><span class="line">    bank.transfer(from,to.amount);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>上面的逻辑存在一个问题，当通过<code>if</code>判断后，假如线程立刻被中断，且中断后其他线程将待转出存款的账户余额修改成低于<code>amount</code>的数值，之后继续执行<code>transfer</code>，但账户余额却不足以转款操作了。</p>
<p>可以使用条件对象优化上面的判断逻辑。</p>
<p>一个锁对象可以有一个或多个相关联的条件对象。可以用<code>ReentrantLock.newCondition()</code>方法获得一个条件对象（<code>Condition</code>）。当某个条件不通过时，可以调用<code>Condition.await()</code>方法使当前线程暂停，并放弃锁。这就允许其他线程执行，不满足继续执行条件而暂停的线程将会进入这个条件的<strong>等待集（wait set）</strong>，并期望其他线程的操作可以使得不满足的条件得到满足。当另一个线程完成对等操作后，需要调用<code>Condition.signalAll()</code>方法，这会重新激活等待这个条件的所有线程。这些线程会尝试重新进入该对象，一旦锁可用，它们中的某个线程将从<code>await()</code>调用返回，得到这个锁，<strong>从之前暂停的地方继续执行</strong>。</p>
<p>通常，<code>await()</code>调用应该放在如下形式的循环中：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span>(!(OK to proceed)) </span><br><span class="line">    condition.await();</span><br></pre></td></tr></tbody></table></figure>
<p>下面我们来看一个例子，利用条件对象来完善转账功能：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Bank</span> {</span><br><span class="line">    <span class="keyword">private</span> <span class="type">var</span> <span class="variable">bankLock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantLock</span>();</span><br><span class="line">    <span class="keyword">private</span> Contition sufficientFunds;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Bank</span><span class="params">()</span> {</span><br><span class="line">        sufficientFunds = bankLock.newCondition();</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">transfer</span><span class="params">(<span class="type">int</span> form,<span class="type">int</span> to,<span class="type">int</span> amount)</span> {</span><br><span class="line">        bankLock.lock();</span><br><span class="line">        <span class="keyword">try</span> {</span><br><span class="line">            <span class="keyword">while</span>(accounts[from] &lt; amount)</span><br><span class="line">                sufficientFunds.await(); <span class="comment">// 不满足条件，进入等待集</span></span><br><span class="line">            System.out.println(Thread.currentThread());</span><br><span class="line">            accounts[form] - = amount;</span><br><span class="line">            accounts[to] += amount;</span><br><span class="line">            sufficientFunds.signalAll(); <span class="comment">// 激活其他线程</span></span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">finally</span> {</span><br><span class="line">            bankLock.unlock();</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<h2 id="synchronized关键字"><a href="#synchronized关键字" class="headerlink" title="synchronized关键字"></a>synchronized关键字</h2><p><code>Lock</code>和<code>Condition</code>接口的使用很麻烦，可以使用Java的另一种规则。Java每个对象都有一个内部锁。</p>
<p>如果一个方法声明时有<code>synchronized</code>关键字，那么对象的锁将保护整个方法。也就是说，要调用这个方法，进程必须获得内部对象锁。</p>
<p>内部对象锁只有一个关联条件。<code>wait()</code>方法将一个线程增加到<strong>等待集</strong>中，<code>notifyAll()</code>/<code>notify()</code>方法可以解除等待线程的阻塞。</p>
<p>例如，可以如下实现<code>Bank</code>类：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Bank</span> {</span><br><span class="line">    <span class="keyword">private</span>[] accounts;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title function_">transfer</span><span class="params">(<span class="type">int</span> from,<span class="type">int</span> to,<span class="type">int</span> amount)</span></span><br><span class="line">    	<span class="keyword">throws</span> InterruptedException</span><br><span class="line">    {</span><br><span class="line">        <span class="keyword">while</span>(accounts[from] &lt; amount)</span><br><span class="line">            wait();</span><br><span class="line">        accounts[from] -= amount;</span><br><span class="line">        accounts[to] += amount;</span><br><span class="line">        notifyAll();</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://xiaoqingming18.github.io/2023/06/10/WEB%E5%89%8D%E7%AB%AF%E5%A4%A7%E5%B8%88%E8%AF%BE%E7%AC%94%E8%AE%B0/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="鸣蜩十七">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="鸣蜩十七">
      <meta itemprop="description" content="记录我的学习、生活和恋爱日常">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 鸣蜩十七">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2023/06/10/WEB%E5%89%8D%E7%AB%AF%E5%A4%A7%E5%B8%88%E8%AF%BE%E7%AC%94%E8%AE%B0/" class="post-title-link" itemprop="url">WEB前端大师课笔记</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2023-06-10 21:02:29" itemprop="dateCreated datePublished" datetime="2023-06-10T21:02:29+08:00">2023-06-10</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-06-11 17:34:51" itemprop="dateModified" datetime="2023-06-11T17:34:51+08:00">2023-06-11</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%89%8D%E7%AB%AF/" itemprop="url" rel="index"><span itemprop="name">-前端</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="事件循环"><a href="#事件循环" class="headerlink" title="事件循环"></a>事件循环</h1><h2 id="浏览器的进程模型"><a href="#浏览器的进程模型" class="headerlink" title="浏览器的进程模型"></a>浏览器的进程模型</h2><h3 id="何为进程？"><a href="#何为进程？" class="headerlink" title="何为进程？"></a>何为进程？</h3><p>程序运行需要有它自己专属的内存空间，可以把这块内存空间理解为进程。</p>
<p>每个应用至少有一个进程，<strong>进程之间相互独立</strong>，即使要通信，也需要双方同意。</p>
<h3 id="何为线程？"><a href="#何为线程？" class="headerlink" title="何为线程？"></a>何为线程？</h3><p>有了进程后，就可以运行程序的代码了。</p>
<p>运行代码的“人”称之为“线程”。</p>
<p><strong>一个进程至少有一个线程</strong>，所以在进程开启后会自动创建一个线程来运行代码，该线程称之为<strong>主线程</strong>。</p>
<p>如果程序需要同时执行多块代码，主线程就会启动更多的线程来执行代码，所以一个进程可以包含多个线程。</p>
<h3 id="浏览器有哪些进程和线程？"><a href="#浏览器有哪些进程和线程？" class="headerlink" title="浏览器有哪些进程和线程？"></a>浏览器有哪些进程和线程？</h3><p><strong>浏览器是一个多进程多线程的应用程序</strong></p>
<p>浏览器内部工作极其复杂，为了避免相互影响，减少连环崩溃的几率，当启动浏览器后，它就会启动多个进程。</p>
<p><img src="/2023/06/10/WEB%E5%89%8D%E7%AB%AF%E5%A4%A7%E5%B8%88%E8%AF%BE%E7%AC%94%E8%AE%B0/浏览器多进程多线程.png" alt="浏览器多进程多线程"></p>
<p>实际上浏览器有多个进程，图中只是举出三个作为例子而已。</p>
<p>其中，最主要的线程有：</p>
<ol>
<li><p>浏览器进程</p>
<p>主要负责界面显示、用户交互、子进程管理等。浏览器进程内部会启动多个线程处理不同的任务。</p>
</li>
<li><p>网络进程</p>
<p>负责加载网络资源，网络进程内部会启动多个线程来处理不同的网络任务。</p>
</li>
<li><p><strong>渲染进程</strong>（重点）</p>
<p>渲染进程启动后，会开启一个<strong>渲染主线程</strong>，主线程负责执行HTML、CSS、JS代码。</p>
<p>默认情况下，<strong>浏览器会为每个标签页开启一个新的渲染进程</strong>，以保证不同的标签页之间不相互影响。（将来该默认模式可能会有所改变，具体参考Chrome官方文档）</p>
</li>
</ol>
<h2 id="渲染主线程是如何工作的"><a href="#渲染主线程是如何工作的" class="headerlink" title="渲染主线程是如何工作的"></a>渲染主线程是如何工作的</h2><p>渲染主线程是浏览器中最繁忙的线程，需要它处理的任务包括但不限于：</p>
<ul>
<li>解析HTML</li>
<li>解析CSS</li>
<li>计算样式</li>
<li>布局</li>
<li>处理图层</li>
<li>每秒把页面画60次</li>
<li>执行全局JS代码</li>
<li>执行事件处理函数</li>
<li>执行计时器的回调函数</li>
<li>······</li>
</ul>
<blockquote>
<p><sup><a href="#fn_" id="reffn_"></a></sup>: 思考题：为什么渲染进程不适用多个进程来处理这些事情？</p>
</blockquote>
<p>要处理这么多任务，主线程如何调度任务呢？</p>
<p>比如，正在执行一个JS函数，执行到一半用户点击了某个按钮，JS该立即去执行事件处理函数吗？</p>
<p>渲染主线程想了一个办法来处理这种问题：排队</p>
<p><img src="/2023/06/10/WEB%E5%89%8D%E7%AB%AF%E5%A4%A7%E5%B8%88%E8%AF%BE%E7%AC%94%E8%AE%B0/排队.png" alt="排队"></p>
<ol>
<li>在最开始的时候，渲染主线程会进入一个无限循环</li>
<li>每一次循环会检查消息队列中是否有任务存在。如果有，就取出第一个任务执行，执行完一个后进入下一次循环;如果没有，则进入休眠状态。</li>
<li>其他所有线程(包括其他进程的线程)可以随时向消息队列添加任务。新任务会加到消息队列的末尾。在添加新任务时，如果主线程是休眠状态，则会将其唤醒以继续循环拿取任务</li>
</ol>
<h2 id="若干解释"><a href="#若干解释" class="headerlink" title="若干解释"></a>若干解释</h2><h3 id="何为异步"><a href="#何为异步" class="headerlink" title="何为异步"></a>何为异步</h3><p>代码在执行过程中，会遇到一些无法立即执行的任务，比如：</p>
<ul>
<li>及时完成后需要执行的任务 — <code>setTimeout</code></li>
<li>网络通信完成后需要执行的任务 — <code>XHR</code></li>
<li>用户操作后需要执行的任务 — <code>addEventListener</code></li>
</ul>
<p>如果让渲染主线程等待这些任务的时机达到，就会导致主线程长期处于「阻塞」的状态，从而导致浏览器「卡死」。</p>
<p>渲染主线程承担着极其重要的任务，无论如何都不能阻塞！</p>
<p>使用异步的方式，渲染主线程用不阻塞：</p>
<p><img src="/2023/06/10/WEB%E5%89%8D%E7%AB%AF%E5%A4%A7%E5%B8%88%E8%AF%BE%E7%AC%94%E8%AE%B0/异步.png" alt="异步"></p>
<blockquote>
<p>面试题：如何理解JS的异步</p>
<p>答案： JS是一门单线程的语言，这是因为它运行在浏览器的渲染主线程中，而渲染主线程只有一个。而渲染主线程承担着诸多的工作，渲染页面、执行 JS 都在其中运行。<br>如果使用同步的方式，就极有可能导致主线程产生阻塞，从而导致消息队列中的很多其他任务无法得到执行。这样一来，一方面会导致繁忙的主线程白白的消耗时间，另一方面导致页面无法及时更新，给用户造成卡死现象。<br>所以浏览器采用异步的方式来避免。具体做法是当某些任务发生时，比如计时器、网络、事件监听，主线程将任务交给其他线程去处理，自身立即结束任务的执行，转而执行后续代码。当其他线程完成时，将事先传递的回调函数包装成任务，加入到消息队列的末尾排队，等待主线程调度执行。<br>在这种异步模式下，浏览器永不阻塞，从而最大限度的保证了单线程的流畅运行。</p>
</blockquote>
<h3 id="JS为何会阻塞渲染"><a href="#JS为何会阻塞渲染" class="headerlink" title="JS为何会阻塞渲染"></a>JS为何会阻塞渲染</h3><p>先看代码：</p>
<figure class="highlight html"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">h1</span>&gt;</span>Mr.Yuan is awesome!<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">button</span>&gt;</span>change<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">var</span> h1 = <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">"h1"</span>);</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">var</span> button = <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">"button"</span>);</span></span><br><span class="line"><span class="language-javascript">    </span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 死循环制定的时间</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">function</span> <span class="title function_">delay</span>(<span class="params">duration</span>) {</span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">var</span> start = <span class="title class_">Date</span>.<span class="title function_">now</span>();</span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">while</span>(<span class="title class_">Date</span>.<span class="title function_">now</span>() - start &lt; duration) {}</span></span><br><span class="line"><span class="language-javascript">    }</span></span><br><span class="line"><span class="language-javascript">    </span></span><br><span class="line"><span class="language-javascript">    btn.<span class="property">onclick</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span></span><br><span class="line"><span class="language-javascript">        h1.<span class="property">textContent</span> = <span class="string">"袁老师很帅"</span>;</span></span><br><span class="line"><span class="language-javascript">        <span class="title function_">delay</span>(<span class="number">3000</span>);</span></span><br><span class="line"><span class="language-javascript">    }</span></span><br><span class="line"><span class="language-javascript"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></tbody></table></figure>
<p>这段代码执行时，点击按钮后，并不是先改变文字再开始死循环，而是先死循环卡顿3000ms，再改变文字。具体细节如下：</p>
<ul>
<li>一开始渲染主线程将点击事件交给浏览器线程监听，此时渲染主线程无事可做。</li>
<li>当用户点击按钮后，浏览器线程将点击事件交给渲染主线程执行，点击事件中包括改变h1标签文字的绘制任务和死循环的delay任务。</li>
<li>渲染主线程执行h1标签文字，但需要绘制才能让用户看到，后面还要执行delay任务，所以此时将绘制任务添加到消息队列，当delay执行死循环结束后，渲染主线程才开始绘制任务，用户才看到文字的变动</li>
</ul>
<p>综上所述，其实文字是在死循环之前就改变了，但是需要等待死循环结束开始绘制任务，用户才能看到改动。</p>
<h3 id="任务有优先级吗"><a href="#任务有优先级吗" class="headerlink" title="任务有优先级吗"></a>任务有优先级吗</h3><p>任务没有优先级，在消息队列中先进先出。</p>
<p>但<strong>消息队列是有优先级的</strong>。</p>
<p>根据W3C的最新解释：</p>
<ul>
<li>每个任务都有一个任务类型，<strong>同一类型的任务必须在同一个队列</strong>，不同类型的任务可以分属不同的队列。在一次事件循环中，浏览器可以根据实际情况从不同的队列中取出任务执行。</li>
<li>浏览器必须准备好一个微队列，微队列中的任务优先所有其他任务执行。</li>
</ul>
<p>在目前chrome的实现中，至少包含了下面的队列：</p>
<ul>
<li>延时队列：用于存放计时器到达后的回调任务，优先级「中」</li>
<li>交互队列：用于存放用户操作后产生的事件处理任务，优先级「高」</li>
<li>微队列：用于存放需要最快执行的任务，优先级「最高」</li>
</ul>
<blockquote>
<p>添加任务到微队列的主要方式是使用 Promise、MutationObserver</p>
<p>例如：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="title class_">Promise</span>.<span class="title function_">resolve</span>().<span class="title function_">then</span>(函数);</span><br></pre></td></tr></tbody></table></figure>
<p>面试题：请简述一下JS的事件循环</p>
<p>答案：事件循环又叫做消息循环，是浏览器渲染主线程的工作方式。<br>在Chrome的源码中，它开启一个不会结束的for循环，每次循环从消息队列中取出第一个任务执行，而其他线程只需要在合适的时候将任务加入到队列末尾即可。<br>过去把消息队列简单分为宏队列和微队列，这种说法目前已无法满足复杂的浏览器环境，取而代之的是一种更加灵活多变的处理方式。<br>根据W3C官方的解释，每个任务有不同的类型，同类型的任务必须在同一个队列，不同的任务可以属于不同的队列。不同任务队列有不同的优先级，在一次事件循环中，由浏览器自行决定取哪一个队列的任务。但浏览器必须有一个微队列，微队列的任务一定具有最高的优先级，必须优先调度执行。</p>
<p>面试题：JS 中的计时器能做到精确计时吗? 为什么?</p>
<p>答案：<strong>不行，因为:</strong></p>
<ol>
<li>操作系统的计时函数本身就有少量偏差，由由于JS 的计时器最终调用的是操作系统的函数，也就携带了 些偏差；</li>
<li>按照 W3C 的标准，浏览器实现计时器时，如果嵌套层级超过5层，则会带有4毫秒的延迟，在计时时间少于4毫秒时又带来了偏差；</li>
<li>受事件循环的影响，计时器的回调函数只能有在主线程空闲时运行，因此又带来了偏差。</li>
</ol>
</blockquote>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>单线程是异步产生的原因</p>
<p>事件循环是异步的实现方式</p>
<h1 id="浏览器渲染原理"><a href="#浏览器渲染原理" class="headerlink" title="浏览器渲染原理"></a>浏览器渲染原理</h1><h2 id="浏览器是如何渲染页面的"><a href="#浏览器是如何渲染页面的" class="headerlink" title="浏览器是如何渲染页面的"></a>浏览器是如何渲染页面的</h2><p>当浏览器的网络线程收到HTML文档后，会产生一个渲染任务，并将其传递给渲染主线程的消息队列。</p>
<p>在事件循环机制的作用下，渲染主线程取出消息队列中的渲染任务，开启渲染流程。</p>
<p>整个渲染流程分为多个阶段，分别是: HTML 解析、样式计算、布局、分层、绘制、分块、光栅化、画</p>
<p>每个阶段都有明确的输入输出，上一个阶段的输 出会成为下一个阶段的输入。</p>
<p>这样，整个渲染流程就形成了一套组织严密的生产流水线：</p>
<p><img src="/2023/06/10/WEB%E5%89%8D%E7%AB%AF%E5%A4%A7%E5%B8%88%E8%AF%BE%E7%AC%94%E8%AE%B0/渲染流水线.png" alt="渲染流水线"></p>
<h3 id="渲染的第一步：解析HTML——Parse-HTML"><a href="#渲染的第一步：解析HTML——Parse-HTML" class="headerlink" title="渲染的第一步：解析HTML——Parse HTML"></a>渲染的第一步：解析HTML——Parse HTML</h3><p>渲染的第一步就是解析HTML字符串。</p>
<p>解析过程中遇到CSS解析CSS, 遇到JS执行JS。 为了提高解析效率,浏览器在开始解析前，会启动一个预解析的线程，率先下载HTML 中的外部CSS文件和外部的JS文件。</p>
<p>如果主线程解析到<strong>link</strong>位置，此时外部的CSS文件还没有下载解析好，主线程不会等待，继续解析后续的HTML。这是因为下载和解析CSS的工作是在预解析线程中进行的。这就是CSS不会阻塞HTML解析的根本<br>原因。</p>
<p>如果主线程解析到<strong>script</strong>位置，会停止解析HTML，转而等待JS文件下载好，并将全局代码解析执行完成后，才能继续解析HTML。 这是因为JS代码的执行过程可能会修改当前的DOM树，所以DOM树的生成必须暂停。这就是JS会阻塞HTML 解析的根本原因。</p>
<p>第一步完成后，会得到DOM树和CSSOM树，浏览器的默认样式、内部样式、外部样式、行内样式均会包含<br>在CSSOM树中。</p>
<h3 id="渲染的第二步：样式计算——Recalculate-Style"><a href="#渲染的第二步：样式计算——Recalculate-Style" class="headerlink" title="渲染的第二步：样式计算——Recalculate Style"></a>渲染的第二步：样式计算——Recalculate Style</h3><p>渲染的下一步是样式计算。</p>
<p>主线程会遍历得到的DOM树,依次为树中的每个节点计算出它最终的样式，称之为Computed Style。</p>
<p>在这一过程中，很多预设值会变成绝对值，比如red会变成rgb(255,0,0) ;相对单位会变成绝对单位，比如em会变成px。</p>
<p>这一步完成后，会得到一棵带有样式的DOM树。</p>
<h3 id="渲染的第三步：布局"><a href="#渲染的第三步：布局" class="headerlink" title="渲染的第三步：布局"></a>渲染的第三步：布局</h3><p>接下来是布局，布局完成后会得到布局树。</p>
<p>布局阶段会依次遍历DOM树的每一个节点，计算每个节点的几何信息。例如节点的宽高、相对包含块的位置。</p>
<p>大部分时候，DOM树和布局树并非一一对应。</p>
<p>比如display:none的节点没有几何信息，因此不会生成到布局树;又比如使用了伪元素选择器，虽然DOM树中不存在这些伪元素节点，但它们拥有几何信息，所以会生成到布局树中。还有<strong>匿名行盒</strong>、<strong>匿名块盒</strong>等等都会导致DOM树和布局树无法一一对应。</p>
<h3 id="渲染的第四步：分层"><a href="#渲染的第四步：分层" class="headerlink" title="渲染的第四步：分层"></a>渲染的第四步：分层</h3><p>下一步是分层</p>
<p>主线程会使用一套复杂的策略对整个布局树中进行分层。</p>
<p>分层的好处在于，将来某一个层改变后，仅会对该层进行后续处理，从而提升效率。</p>
<p>滚动条、堆叠上下文、transform、 opacity等样式都会或多或少的影响分层结果，也可以通过will-change属性更大程度的影响分层结果。</p>
<h3 id="渲染的第五步：绘制"><a href="#渲染的第五步：绘制" class="headerlink" title="渲染的第五步：绘制"></a>渲染的第五步：绘制</h3><p>再下一步是绘制</p>
<p>主线程会为每个层单独产生绘制指令集，用于描述这一层的内容该如何画出来。</p>
<h3 id="分块"><a href="#分块" class="headerlink" title="分块"></a>分块</h3><p>完成绘制后，主线程将每个图层的绘制信息提交给合成线程，剩余工作将由合成线程完成。</p>
<p>合成线程首先对每个图层进行分块，将其划分为更多的小区域。它会从线程池中拿取多个线程来完成分块工作。</p>
<h3 id="光栅化"><a href="#光栅化" class="headerlink" title="光栅化"></a>光栅化</h3><p>分块完成后，进入光栅化阶段。</p>
<p>合成线程会将块信息交给GPU 进程，以极高的速度完成光栅化。GPU进程会开启多个线程来完成光栅化，并且优先处理靠近视口区域的块。光栅化的结果，就是一块一块的位图</p>
<h3 id="画——Draw"><a href="#画——Draw" class="headerlink" title="画——Draw"></a>画——Draw</h3><p>最后一个阶段就是画了。</p>
<p>合成线程拿到每个层、每个块的位图后，生成一个个「指引(quad) 」信息。</p>
<p>指引会标识出每个位图应该画到屏幕的哪个位置，以及会考虑到旋转、缩放等变形。</p>
<p>变形（<code>transform</code>）发生在合成线程，与渲染主线程无关，这就是transform效率高的本质原因。</p>
<p>合成线程会把quad提交给GPU 进程，由GPU 进程产生系统调用，提交给GPU 硬件，完成最终的屏幕成像。</p>
<h3 id="渲染完整过程"><a href="#渲染完整过程" class="headerlink" title="渲染完整过程"></a>渲染完整过程</h3><p><img src="/2023/06/10/WEB%E5%89%8D%E7%AB%AF%E5%A4%A7%E5%B8%88%E8%AF%BE%E7%AC%94%E8%AE%B0/渲染完整过程.png" alt="渲染完整过程"></p>
<h2 id="面试题"><a href="#面试题" class="headerlink" title="面试题"></a>面试题</h2><h3 id="什么是reflow？"><a href="#什么是reflow？" class="headerlink" title="什么是reflow？"></a>什么是reflow？</h3><p>reflow的本质就是重新计算layout 树。</p>
<p>当进行了会影响布局树的操作后，需要重新计算布局树，会引发layout。</p>
<p>为了避免连续的多次操作导致布局树反复计算，浏览器会合并这些操作，当JS代码全部完成后再进行统一计算。所以，改动属性造成的reflow 是异步完成的。</p>
<p>也同样因为如此，当JS获取布局属性时，就可能造成无法获取到最新的布局信息。浏览器在反复权衡下，最终决定获取属性立即reflow。</p>
<h3 id="什么是repaint？"><a href="#什么是repaint？" class="headerlink" title="什么是repaint？"></a>什么是repaint？</h3><p>repaint的本质就是重新根据分层信息计算了绘制指令。</p>
<p>当改动了可见样式后，就需要重新计算，会引发repaint。</p>
<p>由于元素的布局信息也属于可见样式，所以reflow一定会引起repaint。</p>
<h3 id="为什么transform效率高？"><a href="#为什么transform效率高？" class="headerlink" title="为什么transform效率高？"></a>为什么transform效率高？</h3><p>因为transform 既不会影响布局也不会影响绘制指令，它影响的只是渲染流程的最后- -个「draw」 阶段。</p>
<p>由于draw 阶段在合成线程中，所以transform 的变化几乎不会影响渲染主线程。反之，渲染主线程无论<br>如何忙碌，也不会影响transform 的变化。</p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://xiaoqingming18.github.io/2023/06/03/%E9%A1%B5%E5%BC%8F%E5%AD%98%E5%82%A8/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="鸣蜩十七">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="鸣蜩十七">
      <meta itemprop="description" content="记录我的学习、生活和恋爱日常">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 鸣蜩十七">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2023/06/03/%E9%A1%B5%E5%BC%8F%E5%AD%98%E5%82%A8/" class="post-title-link" itemprop="url">未命名</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2023-06-03 16:40:18" itemprop="dateCreated datePublished" datetime="2023-06-03T16:40:18+08:00">2023-06-03</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-06-04 15:32:48" itemprop="dateModified" datetime="2023-06-04T15:32:48+08:00">2023-06-04</time>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="页式存储"><a href="#页式存储" class="headerlink" title="页式存储"></a>页式存储</h1><p> <img src="/2023/06/03/%E9%A1%B5%E5%BC%8F%E5%AD%98%E5%82%A8/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603161444378.png" alt="image-20230603161444378"></p>
<h1 id="虚地址和实地址"><a href="#虚地址和实地址" class="headerlink" title="虚地址和实地址"></a>虚地址和实地址</h1><h2 id="虚地址与实地址介绍"><a href="#虚地址与实地址介绍" class="headerlink" title="虚地址与实地址介绍"></a>虚地址与实地址介绍</h2><p><img src="/2023/06/03/%E9%A1%B5%E5%BC%8F%E5%AD%98%E5%82%A8/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603161942047.png" alt="image-20230603161942047"></p>
<h2 id="页表"><a href="#页表" class="headerlink" title="页表"></a>页表</h2><p><img src="/2023/06/03/%E9%A1%B5%E5%BC%8F%E5%AD%98%E5%82%A8/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603162355831.png" alt="image-20230603162355831"></p>
<h2 id="地址变换过程"><a href="#地址变换过程" class="headerlink" title="地址变换过程"></a>地址变换过程</h2><p><img src="/2023/06/03/%E9%A1%B5%E5%BC%8F%E5%AD%98%E5%82%A8/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603163121492.png" alt="image-20230603163121492"></p>
<p>将近期访问过的页表项放入更高速的储存器，可加快地址变换的速度，这就引入了下面的知识点</p>
<h2 id="地址变换过程（快表TLB）"><a href="#地址变换过程（快表TLB）" class="headerlink" title="地址变换过程（快表TLB）"></a>地址变换过程（快表TLB）</h2><p><img src="/2023/06/03/%E9%A1%B5%E5%BC%8F%E5%AD%98%E5%82%A8/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603163736185.png" alt="image-20230603163736185"></p>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p><img src="/2023/06/03/%E9%A1%B5%E5%BC%8F%E5%AD%98%E5%82%A8/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603163928923.png" alt="image-20230603163928923"></p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://xiaoqingming18.github.io/2023/06/03/%E8%AE%A1%E7%BB%84%EF%BC%9Acache%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%92%8C%E5%8E%9F%E7%90%86/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="鸣蜩十七">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="鸣蜩十七">
      <meta itemprop="description" content="记录我的学习、生活和恋爱日常">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 鸣蜩十七">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2023/06/03/%E8%AE%A1%E7%BB%84%EF%BC%9Acache%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%92%8C%E5%8E%9F%E7%90%86/" class="post-title-link" itemprop="url">未命名</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2023-06-03 16:08:40" itemprop="dateCreated datePublished" datetime="2023-06-03T16:08:40+08:00">2023-06-03</time>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="cache的基本概念和原理"><a href="#cache的基本概念和原理" class="headerlink" title="cache的基本概念和原理"></a>cache的基本概念和原理</h1><p><img src="/2023/06/03/%E8%AE%A1%E7%BB%84%EF%BC%9Acache%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%92%8C%E5%8E%9F%E7%90%86/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603145452291.png" alt="image-20230603145452291"></p>
<p><img src="/2023/06/03/%E8%AE%A1%E7%BB%84%EF%BC%9Acache%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%92%8C%E5%8E%9F%E7%90%86/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603145604728.png" alt="image-20230603145604728"></p>
<p><img src="/2023/06/03/%E8%AE%A1%E7%BB%84%EF%BC%9Acache%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%92%8C%E5%8E%9F%E7%90%86/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603150115456.png" alt="image-20230603150115456"></p>
<p><img src="/2023/06/03/%E8%AE%A1%E7%BB%84%EF%BC%9Acache%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%92%8C%E5%8E%9F%E7%90%86/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603150356449.png" alt="image-20230603150356449"></p>
<p><img src="/2023/06/03/%E8%AE%A1%E7%BB%84%EF%BC%9Acache%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%92%8C%E5%8E%9F%E7%90%86/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603150421798.png" alt="image-20230603150421798"></p>
<h1 id="cache和主存的映射方式"><a href="#cache和主存的映射方式" class="headerlink" title="cache和主存的映射方式"></a>cache和主存的映射方式</h1><p><img src="/2023/06/03/%E8%AE%A1%E7%BB%84%EF%BC%9Acache%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%92%8C%E5%8E%9F%E7%90%86/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603151223985.png" alt="image-20230603151223985"></p>
<h2 id="全相联映射"><a href="#全相联映射" class="headerlink" title="全相联映射"></a>全相联映射</h2><p><img src="/2023/06/03/%E8%AE%A1%E7%BB%84%EF%BC%9Acache%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%92%8C%E5%8E%9F%E7%90%86/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603152219630.png" alt="image-20230603152219630"></p>
<h2 id="直接映射"><a href="#直接映射" class="headerlink" title="直接映射"></a>直接映射</h2><p><img src="/2023/06/03/%E8%AE%A1%E7%BB%84%EF%BC%9Acache%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%92%8C%E5%8E%9F%E7%90%86/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603152534390.png" alt="image-20230603152534390"></p>
<p><img src="/2023/06/03/%E8%AE%A1%E7%BB%84%EF%BC%9Acache%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%92%8C%E5%8E%9F%E7%90%86/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603152604315.png" alt="image-20230603152604315"></p>
<p><img src="/2023/06/03/%E8%AE%A1%E7%BB%84%EF%BC%9Acache%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%92%8C%E5%8E%9F%E7%90%86/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603152742217.png" alt="image-20230603152742217"></p>
<h2 id="组相联映射"><a href="#组相联映射" class="headerlink" title="组相联映射"></a>组相联映射</h2><p><img src="/2023/06/03/%E8%AE%A1%E7%BB%84%EF%BC%9Acache%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%92%8C%E5%8E%9F%E7%90%86/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603153025812.png" alt="image-20230603153025812"></p>
<p><img src="/2023/06/03/%E8%AE%A1%E7%BB%84%EF%BC%9Acache%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%92%8C%E5%8E%9F%E7%90%86/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603153137313.png" alt="image-20230603153137313"></p>
<h1 id="cache替换算法"><a href="#cache替换算法" class="headerlink" title="cache替换算法"></a>cache替换算法</h1><p><img src="/2023/06/03/%E8%AE%A1%E7%BB%84%EF%BC%9Acache%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%92%8C%E5%8E%9F%E7%90%86/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603153430826.png" alt="image-20230603153430826"></p>
<p><img src="/2023/06/03/%E8%AE%A1%E7%BB%84%EF%BC%9Acache%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%92%8C%E5%8E%9F%E7%90%86/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603153440505.png" alt="image-20230603153440505"></p>
<h2 id="随机算法"><a href="#随机算法" class="headerlink" title="随机算法"></a>随机算法</h2><p><img src="/2023/06/03/%E8%AE%A1%E7%BB%84%EF%BC%9Acache%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%92%8C%E5%8E%9F%E7%90%86/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603153845816.png" alt="image-20230603153845816"></p>
<h2 id="先进先出算法"><a href="#先进先出算法" class="headerlink" title="先进先出算法"></a>先进先出算法</h2><p><img src="/2023/06/03/%E8%AE%A1%E7%BB%84%EF%BC%9Acache%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%92%8C%E5%8E%9F%E7%90%86/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603154025987.png" alt="image-20230603154025987"></p>
<h2 id="近期最少使用算法"><a href="#近期最少使用算法" class="headerlink" title="近期最少使用算法"></a>近期最少使用算法</h2><p><img src="/2023/06/03/%E8%AE%A1%E7%BB%84%EF%BC%9Acache%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%92%8C%E5%8E%9F%E7%90%86/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603154853587.png" alt="image-20230603154853587"></p>
<p><img src="/2023/06/03/%E8%AE%A1%E7%BB%84%EF%BC%9Acache%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%92%8C%E5%8E%9F%E7%90%86/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603155053968.png" alt="image-20230603155053968"></p>
<h2 id="最不经常使用算法"><a href="#最不经常使用算法" class="headerlink" title="最不经常使用算法"></a>最不经常使用算法</h2><p><img src="/2023/06/03/%E8%AE%A1%E7%BB%84%EF%BC%9Acache%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%92%8C%E5%8E%9F%E7%90%86/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603155433543.png" alt="image-20230603155433543"></p>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p><img src="/2023/06/03/%E8%AE%A1%E7%BB%84%EF%BC%9Acache%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%92%8C%E5%8E%9F%E7%90%86/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603155457493.png" alt="image-20230603155457493"></p>
<h1 id="cache写策略"><a href="#cache写策略" class="headerlink" title="cache写策略"></a>cache写策略</h1><p><img src="/2023/06/03/%E8%AE%A1%E7%BB%84%EF%BC%9Acache%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%92%8C%E5%8E%9F%E7%90%86/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603155549659.png" alt="image-20230603155549659"></p>
<p><img src="/2023/06/03/%E8%AE%A1%E7%BB%84%EF%BC%9Acache%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%92%8C%E5%8E%9F%E7%90%86/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603155620108.png" alt="image-20230603155620108"></p>
<h1 id="写命中"><a href="#写命中" class="headerlink" title="写命中"></a>写命中</h1><h2 id="写回法"><a href="#写回法" class="headerlink" title="写回法"></a>写回法</h2><p><img src="/2023/06/03/%E8%AE%A1%E7%BB%84%EF%BC%9Acache%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%92%8C%E5%8E%9F%E7%90%86/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603155902784.png" alt="image-20230603155902784"></p>
<h2 id="全写法（写直通法）"><a href="#全写法（写直通法）" class="headerlink" title="全写法（写直通法）"></a>全写法（写直通法）</h2><p><img src="/2023/06/03/%E8%AE%A1%E7%BB%84%EF%BC%9Acache%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%92%8C%E5%8E%9F%E7%90%86/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603155945392.png" alt="image-20230603155945392"></p>
<p><img src="/2023/06/03/%E8%AE%A1%E7%BB%84%EF%BC%9Acache%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%92%8C%E5%8E%9F%E7%90%86/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603160108295.png" alt="image-20230603160108295"></p>
<p><img src="/2023/06/03/%E8%AE%A1%E7%BB%84%EF%BC%9Acache%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%92%8C%E5%8E%9F%E7%90%86/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603160146079.png" alt="image-20230603160146079"></p>
<h1 id="写不命中"><a href="#写不命中" class="headerlink" title="写不命中"></a>写不命中</h1><h2 id="写分配法"><a href="#写分配法" class="headerlink" title="写分配法"></a>写分配法</h2><p><img src="/2023/06/03/%E8%AE%A1%E7%BB%84%EF%BC%9Acache%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%92%8C%E5%8E%9F%E7%90%86/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603160241144.png" alt="image-20230603160241144"></p>
<h2 id="非写分配法"><a href="#非写分配法" class="headerlink" title="非写分配法"></a>非写分配法</h2><p><img src="/2023/06/03/%E8%AE%A1%E7%BB%84%EF%BC%9Acache%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%92%8C%E5%8E%9F%E7%90%86/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603160342541.png" alt="image-20230603160342541"></p>
<h2 id="多级cache"><a href="#多级cache" class="headerlink" title="多级cache"></a>多级cache</h2><p><img src="/2023/06/03/%E8%AE%A1%E7%BB%84%EF%BC%9Acache%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%92%8C%E5%8E%9F%E7%90%86/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603160626797.png" alt="image-20230603160626797"></p>
<h2 id="总结-1"><a href="#总结-1" class="headerlink" title="总结"></a>总结</h2><p><img src="/2023/06/03/%E8%AE%A1%E7%BB%84%EF%BC%9Acache%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%92%8C%E5%8E%9F%E7%90%86/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230603160806735.png" alt="image-20230603160806735"></p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://xiaoqingming18.github.io/2023/06/01/Java%EF%BC%9A%E6%95%B0%E6%8D%AE%E5%BA%93%E7%BC%96%E7%A8%8B/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="鸣蜩十七">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="鸣蜩十七">
      <meta itemprop="description" content="记录我的学习、生活和恋爱日常">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 鸣蜩十七">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2023/06/01/Java%EF%BC%9A%E6%95%B0%E6%8D%AE%E5%BA%93%E7%BC%96%E7%A8%8B/" class="post-title-link" itemprop="url">Java：数据库编程</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2023-06-01 18:46:33" itemprop="dateCreated datePublished" datetime="2023-06-01T18:46:33+08:00">2023-06-01</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-06-02 09:54:27" itemprop="dateModified" datetime="2023-06-02T09:54:27+08:00">2023-06-02</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%90%8E%E7%AB%AF/" itemprop="url" rel="index"><span itemprop="name">-后端</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="JDBC及其配置"><a href="#JDBC及其配置" class="headerlink" title="JDBC及其配置"></a>JDBC及其配置</h1><h2 id="JDBC介绍"><a href="#JDBC介绍" class="headerlink" title="JDBC介绍"></a>JDBC介绍</h2><p>JDBC（Java Database Connectivity）是Java编程语言的一种标准API，用于与数据库进行交互。它提供了一组用于执行SQL语句、访问和处理数据库的方法和接口。</p>
<p>JDBC允许Java应用程序通过标准的数据库操作语言（如SQL）与各种关系型数据库进行通信，包括MySQL、Oracle、SQL Server等。它提供了一种统一的编程接口，使得开发人员可以编写与数据库无关的代码，从而实现数据库的访问和操作。</p>
<p>以下是一些JDBC的关键概念和组件：</p>
<ol>
<li>DriverManager：JDBC的入口点，用于获取数据库连接。</li>
<li>Connection：表示与数据库的连接，用于创建和执行SQL语句。</li>
<li>Statement和PreparedStatement：用于执行SQL语句的接口，可以执行查询、插入、更新等操作。</li>
<li>ResultSet：表示查询结果集的接口，用于遍历和访问查询结果。</li>
<li>Driver：JDBC驱动程序，实现了JDBC接口，负责与特定数据库进行通信。</li>
</ol>
<p>JDBC的工作流程通常包括以下步骤：</p>
<ol>
<li>加载数据库驱动程序：使用Class.forName()方法加载特定数据库的驱动程序，例如”com.mysql.jdbc.Driver”。</li>
<li>建立数据库连接：通过DriverManager.getConnection()方法获取与数据库的连接，需要提供数据库的URL、用户名和密码。</li>
<li>创建Statement或PreparedStatement：通过Connection对象的createStatement()或prepareStatement()方法创建Statement或PreparedStatement对象，用于执行SQL语句。</li>
<li>执行SQL语句：使用Statement或PreparedStatement对象的executeQuery()、executeUpdate()等方法执行SQL语句，获取查询结果或影响的行数。</li>
<li>处理查询结果：如果执行的是查询语句，可以使用ResultSet对象遍历和访问查询结果。</li>
<li>关闭资源：在不再需要时，关闭ResultSet、Statement、Connection等对象，释放数据库资源。</li>
</ol>
<h2 id="连接到数据库"><a href="#连接到数据库" class="headerlink" title="连接到数据库"></a>连接到数据库</h2><p><code>JDBC</code>连接到数据库需要提供有关数据库的三个参数：数据库URL、用户名和密码。</p>
<h3 id="加载数据库驱动程序"><a href="#加载数据库驱动程序" class="headerlink" title="加载数据库驱动程序"></a>加载数据库驱动程序</h3><p>使用Class.forName()方法加载特定数据库的驱动程序，例如加载Sql Server数据库驱动程序：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Class.forName(<span class="string">"com.microsoft.sqlserver.jdbc.SQLServerDriver"</span>);</span><br></pre></td></tr></tbody></table></figure>
<h3 id="数据库URL"><a href="#数据库URL" class="headerlink" title="数据库URL"></a>数据库URL</h3><p>数据库URL的语法格式可以根据不同的数据库类型而有所差异，下面是Sql Server数据库的URL格式：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">jdbc:sqlserver:<span class="comment">//hostname:port;databaseName=database</span></span><br><span class="line"><span class="comment">// hostname表示数据库服务器的主机名或IP地址</span></span><br><span class="line"><span class="comment">// port表示数据库服务器的端口号（默认为1433）</span></span><br><span class="line"><span class="comment">// database表示要连接的数据库名称</span></span><br></pre></td></tr></tbody></table></figure>
<p>有时根据使用的数据库管理系统不同，还需要在数据库URL中加入一些参数，比如使用最新版Sql Server就需要在数据库URL中加入三个参数，<code>integratedSecurity=false</code>、<code>encrypt=false</code>和<code>trustServerCertificate=false</code>。</p>
<p>所以，使用Sql Server，最终的数据库URL如下所示：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">jdbc:sqlserver:<span class="comment">//hostname:port;databaseName=database;integratedSecurity=false;encrypt=false;trustServerCertificate=false</span></span><br></pre></td></tr></tbody></table></figure>
<h3 id="DriverManager-connection"><a href="#DriverManager-connection" class="headerlink" title="DriverManager.connection"></a>DriverManager.connection</h3><p><code>DriverManager.connection(dbURL,username,password)</code>方法用来连接到数据库，接收三个参数，数据库URL、用户名和密码。返回一个<code>Connection</code>对象，它表示应用程序与数据库之间的通信通道，用于执行SQL语句、提交事务和管理数据库连接。</p>
<h3 id="连接数据库"><a href="#连接数据库" class="headerlink" title="连接数据库"></a>连接数据库</h3><p>以Sql Server为例，像下面这样连接到数据库：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 指定要连接的数据库名称</span></span><br><span class="line"><span class="type">String</span> <span class="variable">dateBaseName</span> <span class="operator">=</span> <span class="string">"XSCJ"</span>;</span><br><span class="line"><span class="comment">// 指定数据库URL</span></span><br><span class="line"><span class="type">String</span> <span class="variable">dbURL</span> <span class="operator">=</span> <span class="string">"jdbc:sqlserver://localhost:1433;databaseName="</span> + dateBaseName+<span class="string">";integratedSecurity=false;"</span>+<span class="string">"encrypt=false;trustServerCertificate=false"</span>;</span><br><span class="line"><span class="comment">// 指定用户名和密码</span></span><br><span class="line"><span class="type">String</span> <span class="variable">username</span> <span class="operator">=</span> <span class="string">"username"</span>;</span><br><span class="line"><span class="type">String</span> <span class="variable">password</span> <span class="operator">=</span> <span class="string">"password"</span>;</span><br><span class="line"><span class="comment">// 连接到数据库</span></span><br><span class="line"><span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> DriverManager.getConnection(dbURL,username,password);</span><br></pre></td></tr></tbody></table></figure>
<h1 id="使用JDBC语句"><a href="#使用JDBC语句" class="headerlink" title="使用JDBC语句"></a>使用JDBC语句</h1><p>JDBC语句的主要作用是与数据库进行交互，执行数据库操作。通过JDBC语句，可以实现数据查询、数据更新、事务管理和数据库管理等功能。</p>
<h2 id="执行SQL语句"><a href="#执行SQL语句" class="headerlink" title="执行SQL语句"></a>执行SQL语句</h2><p>执行SQL语句需要一个<code>Statement</code>对象，所以我们需要先了解<code>Statement</code>对象。</p>
<h3 id="Statement对象"><a href="#Statement对象" class="headerlink" title="Statement对象"></a>Statement对象</h3><p><code>Statement</code>对象是Java JDBC中用于执行SQL语句的接口之一。它用于发送SQL语句到数据库并执行操作，<strong>通常用于执行静态SQL语句</strong>，即在编译时就确定的SQL语句。</p>
<p>可以通过连接数据库时返回的<code>Connection</code>对象的<code>createStatement()</code>方法创建<code>Statement</code>对象：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Statement</span> <span class="variable">statement</span> <span class="operator">=</span> connection.createStatement();</span><br></pre></td></tr></tbody></table></figure>
<h3 id="通过Statement对象执行静态SQL语句"><a href="#通过Statement对象执行静态SQL语句" class="headerlink" title="通过Statement对象执行静态SQL语句"></a>通过Statement对象执行静态SQL语句</h3><p>首先，把要执行的SQL语句放入一个字符串，比如：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">command</span> <span class="operator">=</span> <span class="string">"UPDATE *"</span></span><br><span class="line">    +<span class="string">" FROM Student"</span>;</span><br></pre></td></tr></tbody></table></figure>
<p>要注意的是，如果SQL语句换行，<strong>每一行的开头需要空一格，否则拼接后的SQL语句会缺少空格</strong>。</p>
<p>然后调用<code>Statement</code>对象的三个方法来执行SQL语句，这三个方法分别是：</p>
<ul>
<li>execute()：最通用的执行SQL语句的方法，可以执行任意类型的SQL语句，返回结果为布尔值，表示是否执行了查询语句。</li>
<li>executeQuery()：用于执行SELECT查询语句，返回查询结果的ResultSet对象。</li>
<li>executeUpdate()：用于执行更新语句，返回受影响的行数。</li>
</ul>
<h3 id="通过ReaultSet查看查询结果"><a href="#通过ReaultSet查看查询结果" class="headerlink" title="通过ReaultSet查看查询结果"></a>通过ReaultSet查看查询结果</h3><p><code>ResultSet</code>对象也称结果集。可以使用 ResultSet 对象来遍历和访问查询结果中的数据。<strong><code>ResultSet</code>对象通过调用<code>executeQuery()</code>方法执行查询操作来返回</strong>:</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Result</span> <span class="variable">result</span> <span class="operator">=</span> statement.executeQuery(command);</span><br></pre></td></tr></tbody></table></figure>
<p>分析结果集可以使用与下面类似的循环语句：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span>(result.next()) {</span><br><span class="line">    <span class="comment">// do something</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p><code>ResultSet</code>的迭代器初始化在第一行之前，必须调用<code>next</code>方法将迭代器移动到第一行，另外，它没有<code>hasNext</code>方法，需要不断的调用<code>next</code>方法，知道该方法返回<code>false</code>停止。</p>
<p>结果集中行的顺序是任意排列的，除非使用<code>ORDER BY</code>子句指定行的顺序。</p>
<h3 id="ResultSet访问器"><a href="#ResultSet访问器" class="headerlink" title="ResultSet访问器"></a>ResultSet访问器</h3><p>可以通过<code>ResultSet</code>对象提供的各种<strong>访问器</strong>来获取一行中每一列的内容，并且不同的数据类型有不同的访问器，比如<code>getString</code>和<code>getDouble</code>。</p>
<p>每个访问器接受两种类型的参数，一类是数值，一类是字符。当使用数值作为参数时，访问器返回对应第几列的数据。当使用字符串作为参数时，访问器返回对应同名列的数据。</p>
<p>例如，调用<code>result.getString("Sname")</code>会返回列名为<code>Sname</code>的内容。</p>
<h2 id="管理连接、语句和结果集"><a href="#管理连接、语句和结果集" class="headerlink" title="管理连接、语句和结果集"></a>管理连接、语句和结果集</h2><p>每个<code>Connection</code>对象可以创建多个<code>Statement</code>对象，同一个<code>Statement</code>对象可以执行多次不相关的命令和查询。但是，<strong>一个<code>Statement</code>对象只能有一个打开的结果集</strong>。如果需要同时执行多个查询操作，且同时需要分析查询结果，就需要创建多个<code>Statement</code>对象。</p>
<p>使用完<code>Connection</code>、<code>Statement</code>和<code>Connection</code>对象后应立即调用<code>close</code>方法关闭它们，这些对象都使用了规模较大的数据结构，会占用数据库服务器上的有限资源。</p>
<h1 id="SQL异常"><a href="#SQL异常" class="headerlink" title="SQL异常"></a>SQL异常</h1><p>等待补充</p>
<h1 id="查询操作详解"><a href="#查询操作详解" class="headerlink" title="查询操作详解"></a>查询操作详解</h1><h2 id="预备语句"><a href="#预备语句" class="headerlink" title="预备语句"></a>预备语句</h2><p>上面介绍的查询都是基于提前编写好的SQL语句，即静态SQL语句的查询。而下面要介绍的<strong>预备语句</strong>用于执行预编译的 SQL 语句。</p>
<p>预备语句的工作即优势如下：</p>
<ol>
<li>预编译：在创建预备语句时，数据库系统会对 SQL 语句进行预编译，并将其存储在数据库服务器中。这个过程只发生一次。</li>
<li>参数化查询：预备语句允许使用参数化查询，即将查询中的参数位置用占位符（如<code>?</code>）代替。这些占位符可以在执行预备语句之前动态地绑定具体的值。参数化查询可以防止常见的安全漏洞，如 SQL 注入攻击。</li>
<li>提高性能：由于预备语句已经预先编译，数据库系统会将其优化并存储执行计划。当多次执行相同的预备语句时，只需要发送参数值给数据库服务器，而不必重新编译和优化查询，这可以显著提高执行效率。</li>
<li>简化代码：使用预备语句可以简化代码编写过程，避免手动拼接 SQL 字符串带来的麻烦。预备语句提供了一种更结构化的方式来处理 SQL 查询。</li>
</ol>
<h3 id="预编译SQL语句"><a href="#预编译SQL语句" class="headerlink" title="预编译SQL语句"></a>预编译SQL语句</h3><p>在普通的SQL语句中，将查询条件中的每个宿主变量用问号代替，例如：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">command</span> <span class="operator">=</span> <span class="string">"SELECT Sno,Sname"</span></span><br><span class="line">    +<span class="string">" FROM Student"</span></span><br><span class="line">    +<span class="string">" WHERE Sno = ?"</span></span><br></pre></td></tr></tbody></table></figure>
<h3 id="创建预备语句"><a href="#创建预备语句" class="headerlink" title="创建预备语句"></a>创建预备语句</h3><p>创建预备语句，就是创建<code>PreparedStatement</code>对象。</p>
<p><code>PreparedStatement</code>对象是在 Java 数据库编程中用于执行预编译 SQL 语句的特殊对象，可以通过连接数据库时返回的<code>Connection</code>对象的<code>prepareStatement(预编译SQL语句字符串)</code>方法创建:</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">PreparedStatement</span> <span class="variable">preparedStatement</span> <span class="operator">=</span> connection.prepareStatement(command);</span><br></pre></td></tr></tbody></table></figure>
<h3 id="绑定参数"><a href="#绑定参数" class="headerlink" title="绑定参数"></a>绑定参数</h3><p>在执行预备语句之前需要先绑定参数，使用预备语句<code>PreparedStatement</code>对象的<code>setXXX(int n,XXX x)</code>方法为每个占位符设置具体的值，其中 <code>XXX</code> 是对应参数的数据类型，参数<code>n</code>对应第几个占位符，参数<code>x</code>表示用于替代的内容。例如：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">preparedStatement.setString(<span class="number">1</span>,<span class="string">"21044028"</span>);</span><br></pre></td></tr></tbody></table></figure>
<p>如果想要重用已经执行过的预备查询语句，只需要重新绑定参数即可。</p>
<h3 id="执行预备语句"><a href="#执行预备语句" class="headerlink" title="执行预备语句"></a>执行预备语句</h3><p>执行预备语句的方法与之前说的类似，也是返回一个结果集：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">ResultSet</span> <span class="variable">result</span> <span class="operator">=</span> preparedStatement.executeQuery();</span><br></pre></td></tr></tbody></table></figure>
<h1 id="读写LOB"><a href="#读写LOB" class="headerlink" title="读写LOB"></a>读写LOB</h1><h2 id="读LOB"><a href="#读LOB" class="headerlink" title="读LOB"></a>读LOB</h2><p>许多数据库还可以存储大对象，例如图片或其他数据。在SQL中，二进制大对象称为<strong>BLOB</strong>，字符型大对象称为<strong>CLOB</strong>。</p>
<p>要读取LOB，需要执行SELECT语句，然后再<code>ResultSet</code>结果集上调用<code>getBlob(int index)</code>或<code>getClob(int index)</code>方法，这样就可以获得指定列的<code>Blob</code>或<code>Clob</code>类型的对象。</p>
<p>要从<code>Blob</code>对象中获取二进制数据，可以调用<code>getBytes()</code>或<code>getBinaryStream()</code>方法。例如，有一张保存图书封面的表，就可以像下面这样获取一张图像：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">PreparedStatement</span> <span class="variable">preparedStatement</span> <span class="operator">=</span> connection.prepareStatement(<span class="string">"SELECT Cover FROM BookCovers WHERE ISBN = ?"</span>);</span><br><span class="line">preparedStatement.setString(<span class="number">1</span>,<span class="string">"59854126445"</span>);</span><br><span class="line"><span class="keyword">try</span>(<span class="type">ResultSet</span> <span class="variable">result</span> <span class="operator">=</span> preparedStatement.executeQuery())</span><br><span class="line">{</span><br><span class="line">    <span class="keyword">if</span>(result.next()) {</span><br><span class="line">        <span class="type">Blob</span> <span class="variable">coverBlob</span> <span class="operator">=</span> result.getBlob(<span class="number">1</span>);</span><br><span class="line">        <span class="type">Image</span> <span class="variable">coverImage</span> <span class="operator">=</span> ImageIO.read(coverBlob.getBinaryStream());</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>类似的，如果获取了<code>Clob</code>对象，就可以通过调用<code>ResultSet</code>结果集的<code>getSubString</code>或<code>getCharacterStream</code>方法来获取其中的字符数据。这两个方法的详细说明如下：</p>
<ol>
<li><code>getSubString(long columnIndex, long length)</code> ：用于从结果集中获取指定列的数据，并将其作为一个子字符串返回，适用于从数据库中检索字符串类型（如VARCHAR、CHAR等）的列的数据。<ul>
<li><code>columnIndex</code>：表示要获取数据的列的索引（从1开始），或者可以使用列的名称。</li>
<li><code>length</code>：表示要获取的子字符串的长度。</li>
</ul>
</li>
<li><code>getCharacterStream(int columnIndex)</code> ：用于从结果集中获取指定列的数据，并将其作为一个字符流（<code>Reader</code> 对象）返回,适用于从数据库中检索长文本类型（如CLOB）的列的数据。<ul>
<li><code>columnIndex</code>：表示要获取数据的列的索引（从1开始），或者可以使用列的名称。</li>
</ul>
</li>
</ol>
<h2 id="写LOB"><a href="#写LOB" class="headerlink" title="写LOB"></a>写LOB</h2><p>要将LOB置入数据库中，需要在<code>Connection</code>对象上调用<code>createBlob</code>或<code>createClob</code>，然后获取一个用于该LOB的输出流或写出器，写出数据，并将该对象存储到数据库中。下面用一个例子说明如何向数据库中写入图片。</p>
<p>假设现在有一张StudentImage表，现在需要向其中插入某个学生的图片，可以像下面这样做：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 将指定的文件放入输入流中</span></span><br><span class="line"><span class="type">BufferedInputStream</span> <span class="variable">photoInputStream</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BufferedInputStream</span>(<span class="keyword">new</span> <span class="title class_">FileInputStream</span>(file))</span><br><span class="line"><span class="comment">// 创建预备语句，也就是PreparedStatement对象</span></span><br><span class="line"><span class="type">PreparedStatement</span> <span class="variable">preparedStatement</span> <span class="operator">=</span> connection.prepareStatement(<span class="string">"INSERT INTO StudentImage (student_id, photo) VALUES (?, ?)"</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 设置参数</span></span><br><span class="line">preparedStatement.setInt(<span class="number">1</span>, studentId);  <span class="comment">// studentId 是学生的ID</span></span><br><span class="line">preparedStatement.setBinaryStream(<span class="number">2</span>, photoInputStream);  </span><br><span class="line"><span class="comment">// 开始插入</span></span><br><span class="line"><span class="type">int</span> <span class="variable">result</span> <span class="operator">=</span> preparedStatement.executeUpdate();</span><br><span class="line"><span class="keyword">if</span>(result &gt; <span class="number">0</span>) {</span><br><span class="line">    System.out.println(<span class="string">"插入成功"</span>);</span><br><span class="line">}<span class="keyword">else</span> {</span><br><span class="line">    System.out.println(<span class="string">"插入失败"</span>);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<h1 id="可滚动和可更新的结果集"><a href="#可滚动和可更新的结果集" class="headerlink" title="可滚动和可更新的结果集"></a>可滚动和可更新的结果集</h1><p>默认的结果集<code>ResultSet</code>使用<code>next</code>方法迭代遍历结果集中的所有行。可滚动和可更新的结果集是 Java JDBC 中提供的两种扩展功能，用于对查询结果进行更灵活的操作。</p>
<p><code>ResultSet</code>接口提供了两个参数，即结果集的 <code>Type</code>（类型）和 <code>Concurrency</code>（并发性），用于指定结果集的的特性和并发访问方式。</p>
<ol>
<li><code>ResultSet</code> 的类型（Type）：<ul>
<li><code>TYPE_FORWARD_ONLY</code>：默认类型，表示结果集只能向前（从第一行到最后一行）进行遍历，不支持向后导航或随机访问。</li>
<li><code>TYPE_SCROLL_INSENSITIVE</code>：表示结果集可以滚动，允许向前和向后导航，但对结果集所做的更改不会反映到数据库中。</li>
<li><code>TYPE_SCROLL_SENSITIVE</code>：与 <code>TYPE_SCROLL_INSENSITIVE</code> 类似，允许向前和向后导航，但对结果集所做的更改会反映到数据库中，可能会影响其他同时访问该数据的用户。</li>
</ul>
</li>
<li><code>ResultSet</code> 的并发性（Concurrency）：<ul>
<li><code>CONCUR_READ_ONLY</code>：默认并发性，表示结果集是只读的，不能对其进行更新。</li>
<li><code>CONCUR_UPDATABLE</code>：表示结果集是可更新的，允许对结果集中的数据进行修改、插入和删除操作。</li>
</ul>
</li>
</ol>
<p>默认情况下，结果集是不可滚动和不可更新的。为了从查询中获取可滚动和可更新的结果集，必须使用下面的方法得到一个不同的<code>Statement</code>或<code>PreparedStatement</code>对象：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Statement</span> <span class="variable">statement</span> <span class="operator">=</span> connection.createStatement(type,concurrency);</span><br><span class="line"><span class="type">PreparedStatement</span> <span class="variable">preparedStatement</span> <span class="operator">=</span> connection.prepareStatement(command,type,concurrency);</span><br></pre></td></tr></tbody></table></figure>
<h2 id="可滚动结果集"><a href="#可滚动结果集" class="headerlink" title="可滚动结果集"></a>可滚动结果集</h2><p>可以像下面这样先获得一个可滚动结果集：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Statement</span> <span class="variable">statement</span> <span class="operator">=</span> connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);</span><br></pre></td></tr></tbody></table></figure>
<p>可滚动结果集允许在结果集中自由地向前和向后导航，并能够定位到指定的行。</p>
<p>可滚动结果集有一个游标，用以指示当前位置。在结果集上滚动可以使用下面的语句向后滚动（即向上一行滚动）：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(result.previous())···；</span><br></pre></td></tr></tbody></table></figure>
<p>如果游标位于一个实际的行上，该方法返回<code>true</code>，如果游标位于第一行之前，返回<code>false</code>。</p>
<p>也可以使用<code>result.relative(int n)</code>方法将游标向前（下一行）或向后（上一行）移动多行，当<code>n</code>为正数时，游标向前（向下一行）移动，当<code>n</code>为负数时，游标向后（向上一行）移动。如果游标移动到第一行之前或最后一行之后，该方法返回<code>false</code>，且不移动游标。如果游标移动到一个实际的行，则返回<code>true</code>。</p>
<p>下面总结一下可滚动结果集用于导航和定位游标的方法：</p>
<ul>
<li><code>previous()</code>：在结果集上向后滚动（即向上一行滚动）。</li>
<li><code>relative(int rows)</code>：将光标相对于当前位置移动指定的行数。</li>
<li><code>beforeFirst()</code>：将光标移动到结果集的开头之前的位置。</li>
<li><code>afterLast()</code>：将光标移动到结果集的末尾之后的位置。</li>
<li><code>first()</code>：将光标移动到结果集的第一行。</li>
<li><code>last()</code>：将光标移动到结果集的最后一行。</li>
<li><code>absolute(int row)</code>：将光标移动到指定行号的位置。</li>
<li><code>getRow</code>：返回当前游标所在行的行号。</li>
</ul>
<p>通过使用可滚动结果集，可以实现在结果集中进行随机访问和定位。</p>
<h2 id="可更新结果集"><a href="#可更新结果集" class="headerlink" title="可更新结果集"></a>可更新结果集</h2><p>可以向下面这样先获取一个可更新结果集：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Statement</span> <span class="variable">statement</span> <span class="operator">=</span> connection.CreateStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATEABLE);</span><br></pre></td></tr></tbody></table></figure>
<p>默认情况下，结果集是只读的，不能对结果集中的数据进行修改。可更新结果集允许对结果集中的数据进行修改、插入和删除操作。</p>
<p>使用可更新结果集，可以使用以下方法对数据进行操作：</p>
<ul>
<li><code>updateXXX(int columnIndex, value)</code>：更新指定列的数据，其中 <code>XXX</code> 是对应列的数据类型，<code>columnIndex</code> 是列的索引。</li>
<li><code>updateXXX(String columnName, value)</code>：更新指定列的数据，其中<code>columnName</code> 是结果集中的列名，表示要更新的列的名称。</li>
<li><code>insertRow()</code>：在结果集中插入一行新数据。</li>
<li><code>deleteRow()</code>：删除结果集中的当前行。</li>
</ul>
<p>通过使用可更新结果集，可以实现对结果集中的数据进行修改和维护。</p>
<p>下面是一个使用可更新结果集的简单示例：</p>
<p>假设有一个名为 “students” 的表，包含以下列：id（整数类型）、name（字符串类型）和age（整数类型）。</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 创建可更新结果集</span></span><br><span class="line"><span class="type">Statement</span> <span class="variable">stmt</span> <span class="operator">=</span> connection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);</span><br><span class="line"><span class="type">ResultSet</span> <span class="variable">rs</span> <span class="operator">=</span> stmt.executeQuery(<span class="string">"SELECT * FROM students"</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 向结果集插入新的行</span></span><br><span class="line">rs.moveToInsertRow();</span><br><span class="line">rs.updateInt(<span class="string">"id"</span>, <span class="number">1</span>);</span><br><span class="line">rs.updateString(<span class="string">"name"</span>, <span class="string">"John"</span>);</span><br><span class="line">rs.updateInt(<span class="string">"age"</span>, <span class="number">20</span>);</span><br><span class="line">rs.insertRow();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 移动到新插入的行</span></span><br><span class="line">rs.last();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 更新行数据</span></span><br><span class="line">rs.updateString(<span class="string">"name"</span>, <span class="string">"Jane"</span>);</span><br><span class="line">rs.updateInt(<span class="string">"age"</span>, <span class="number">21</span>);</span><br><span class="line">rs.updateRow();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 删除行数据</span></span><br><span class="line">rs.deleteRow();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 关闭结果集和连接</span></span><br><span class="line">rs.close();</span><br><span class="line">connection.close();</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure>
<p><code>moveToInsertRow()</code> 方法是 <code>ResultSet</code> 接口中的一个方法，用于将结果集的光标移动到可插入行的位置。</p>
<p>当使用可更新结果集（<code>ResultSet.CONCUR_UPDATABLE</code>）时，可以使用 <code>moveToInsertRow()</code> 方法将结果集的光标移动到特殊的插入行位置。在插入行位置，可以使用 <code>updateXXX()</code> 方法设置每列的值，然后调用 <code>insertRow()</code> 方法将新的行插入到结果集中。</p>
<p>在具体操作过程中，<code>moveToInsertRow()</code> 方法的调用将会改变结果集的状态，将结果集的光标从当前行移动到插入行位置。这意味着在调用 <code>moveToInsertRow()</code> 方法后，你无法直接访问当前行的数据，而是处于一个准备插入新行的状态。</p>
<p>以下是一个示例，展示了如何使用 <code>moveToInsertRow()</code> 方法插入新的行：</p>
<figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">javaCopy code// 创建可更新结果集</span><br><span class="line">Statement stmt = connection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);</span><br><span class="line">ResultSet rs = stmt.executeQuery("SELECT * FROM students");</span><br><span class="line"></span><br><span class="line">// 移动到插入行位置</span><br><span class="line">rs.moveToInsertRow();</span><br><span class="line"></span><br><span class="line">// 设置每列的值</span><br><span class="line">rs.updateInt("id", 1);</span><br><span class="line">rs.updateString("name", "John");</span><br><span class="line">rs.updateInt("age", 20);</span><br><span class="line"></span><br><span class="line">// 将新的行插入到结果集中</span><br><span class="line">rs.insertRow();</span><br></pre></td></tr></tbody></table></figure>
<p>在上述示例中，我们首先创建了一个可更新结果集，并执行了查询操作。然后，使用 <code>moveToInsertRow()</code> 方法将光标移动到插入行的位置，接下来使用 <code>updateXXX()</code> 方法设置每列的值。最后，调用 <code>insertRow()</code> 方法将新的行插入到结果集中。</p>
<p>注意，使用可更新结果集时，需要确保数据库表具有适当的权限和支持，且在对结果集进行修改之后，要调用 <code>updateRow()</code> 方法来提交更改到数据库。</p>
<p><code>moveToInsertRow()</code> 方法允许在可更新结果集中移动到特定的插入行位置，并在此位置上进行新行的插入操作。</p>
<h1 id="事务"><a href="#事务" class="headerlink" title="事务"></a>事务</h1><p>可以将一组SQL语句构建成一个<strong>事务</strong>。当所有语句都顺利执行后，事务可以被<strong>提交（COMMIT）</strong>。如果其中某个语句遇到错误，那么事务将被<strong>回滚（ROLLBACK）</strong>，撤销遇到错误前的所有操作，就像没有任何语句被执行过一样。</p>
<p>如果将更新语句组合成一个事务，那么事务要么成功地执行所有操作并提交，要么在中间某个位置发生错误，发生错误后，需要执行回<strong>回滚（ROLLBACK）</strong>操作，数据库将自动撤销自上次提交事务以来的所有更新操作产生的影响。</p>
<h2 id="用JDBC对事务编程"><a href="#用JDBC对事务编程" class="headerlink" title="用JDBC对事务编程"></a>用JDBC对事务编程</h2><p>在Java中，可以使用以下步骤来执行事务：</p>
<ol>
<li>获取数据库连接，并将其设置为手动提交模式：<code>connection.setAutoCommit(false)</code>。</li>
<li>获取语句对象<code>Statement</code>。</li>
<li>在事务中执行数据库操作，例如插入、更新或删除数据。</li>
<li>如果所有的数据库操作都成功完成，调用 <code>connection.commit()</code> 提交事务，将修改持久化到数据库中。</li>
<li>如果出现错误或某个操作失败，调用 <code>connection.rollback()</code> 回滚事务，撤销之前的所有修改。</li>
<li>最后，关闭数据库连接。</li>
</ol>
<p>以下是一个简单的示例：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span> {</span><br><span class="line">    <span class="comment">// 获取数据库连接</span></span><br><span class="line">    <span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> DriverManager.getConnection(<span class="string">"jdbc:mysql://localhost:3306/mydatabase"</span>, <span class="string">"username"</span>, <span class="string">"password"</span>);</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 获取语句对象</span></span><br><span class="line">    <span class="type">Statement</span> <span class="variable">statement</span> <span class="operator">=</span> connection.createStatement();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 设置手动提交模式</span></span><br><span class="line">    connection.setAutoCommit(<span class="literal">false</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 执行数据库操作</span></span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    statement.executeUpdate(command_1);</span><br><span class="line">    statement.executeUpdate(command_2);</span><br><span class="line">    statement.executeUpdate(command_3);</span><br><span class="line">	<span class="comment">// ...</span></span><br><span class="line">    <span class="comment">// 提交事务</span></span><br><span class="line">    connection.commit();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 关闭数据库连接</span></span><br><span class="line">    connection.close();</span><br><span class="line">} <span class="keyword">catch</span> (SQLException e) {</span><br><span class="line">    <span class="comment">// 回滚事务</span></span><br><span class="line">    connection.rollback();</span><br><span class="line">    e.printStackTrace();</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure>
<h2 id="批量更新"><a href="#批量更新" class="headerlink" title="批量更新"></a>批量更新</h2><p>批量更新（Batch Update）是一种在Java数据库编程中用于一次性执行多个数据库更新操作的机制，从而提高性能和效率。在使用批量更新时，一个语句序列作为一批操作将同时被收集和提交。</p>
<p>在Java中，可以使用以下步骤执行批量更新：</p>
<ol>
<li>获取数据库连接。</li>
<li>创建一个 <code>Statement</code> 或 <code>PreparedStatement</code> 对象。</li>
<li>将要执行的更新操作添加到批量更新中，可以使用<code>addBatch()</code>方法将每个更新操作添加到批量更新中。</li>
<li>执行批量更新，使用<code>executeBatch()</code>方法执行批量更新操作，该方法将为所有已提交的语句返回一个受影响行数的数组。</li>
<li>获取更新操作的结果，可以使用<code>getUpdateCount()</code>方法获取每个更新操作的影响行数，或者使用<code>getUpdateCounts()</code>方法获取整个批量更新操作的影响行数。</li>
<li>关闭相关的对象和数据库连接。</li>
</ol>
<p>以下是一个简单的示例：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span> {</span><br><span class="line">    <span class="comment">// 获取数据库连接</span></span><br><span class="line">    <span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> DriverManager.getConnection(<span class="string">"jdbc:mysql://localhost:3306/mydatabase"</span>, <span class="string">"username"</span>, <span class="string">"password"</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建 PreparedStatement 对象</span></span><br><span class="line">    <span class="type">PreparedStatement</span> <span class="variable">statement</span> <span class="operator">=</span> connection.prepareStatement(<span class="string">"UPDATE students SET age = ? WHERE id = ?"</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 添加更新操作到批量更新中</span></span><br><span class="line">    statement.setInt(<span class="number">1</span>, <span class="number">20</span>);</span><br><span class="line">    statement.setInt(<span class="number">2</span>, <span class="number">1</span>);</span><br><span class="line">    statement.addBatch();</span><br><span class="line"></span><br><span class="line">    statement.setInt(<span class="number">1</span>, <span class="number">21</span>);</span><br><span class="line">    statement.setInt(<span class="number">2</span>, <span class="number">2</span>);</span><br><span class="line">    statement.addBatch();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 执行批量更新</span></span><br><span class="line">    <span class="type">int</span>[] updateCounts = statement.executeBatch();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取更新操作的结果</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> count : updateCounts) {</span><br><span class="line">        System.out.println(<span class="string">"影响行数: "</span> + count);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 关闭 Statement 和连接</span></span><br><span class="line">    statement.close();</span><br><span class="line">    connection.close();</span><br><span class="line">} <span class="keyword">catch</span> (SQLException e) {</span><br><span class="line">    e.printStackTrace();</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://xiaoqingming18.github.io/2023/05/31/%E6%95%B0%E6%8D%AE%E5%BA%93%E5%8E%9F%E7%90%86%EF%BC%9A%E5%B9%B6%E5%8F%91%E6%8E%A7%E5%88%B6/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="鸣蜩十七">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="鸣蜩十七">
      <meta itemprop="description" content="记录我的学习、生活和恋爱日常">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 鸣蜩十七">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2023/05/31/%E6%95%B0%E6%8D%AE%E5%BA%93%E5%8E%9F%E7%90%86%EF%BC%9A%E5%B9%B6%E5%8F%91%E6%8E%A7%E5%88%B6/" class="post-title-link" itemprop="url">数据库原理：并发控制</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>
      

      <time title="创建时间：2023-05-31 22:03:43 / 修改时间：23:36:38" itemprop="dateCreated datePublished" datetime="2023-05-31T22:03:43+08:00">2023-05-31</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E6%95%B0%E6%8D%AE%E5%BA%93%E5%8E%9F%E7%90%86/" itemprop="url" rel="index"><span itemprop="name">-数据库原理</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="并发介绍"><a href="#并发介绍" class="headerlink" title="并发介绍"></a>并发介绍</h1><h2 id="串行执行与并行执行"><a href="#串行执行与并行执行" class="headerlink" title="串行执行与并行执行"></a>串行执行与并行执行</h2><p>数据库是一个共享资源，允许多个用户同时使用同一个数据库的数据库系统称为多用户数据库系统。例如飞机订票数据库系统、银行数据库系统等都是多用户数据库系统。在这样的系统中，在同一时刻并发运行的事务可达数百上千个。</p>
<p><strong>事务可以一个一个地串行执行，即每个时刻只有一个事务运行</strong>，其他事务必须等到上一个事务结束后才能运行。为了充分利用系统资源，发挥数据库共享资源的特点，应该允许多个事务并行地执行。</p>
<p>在单处理机系统中，事务的并行执行实际上是这些并行事务的并行操作轮流交叉运行。这种并行执行方式称为<strong>交叉并发方式</strong>。</p>
<p>在多处理机系统中，每个处理机可以运行一个事务，多个处理机可以同时运行多个事务，实现多个事务真正的并发执行。这种并发执行方式称为<strong>同时并发方式</strong>。</p>
<h2 id="为什么要并行执行"><a href="#为什么要并行执行" class="headerlink" title="为什么要并行执行"></a>为什么要并行执行</h2><p>当多个用户并发地存取数据库时，会产生多个事务同时存取同一数据的情况。如果对这种并发操作不加以控制，就可能会存取和存储不正确的数据，破坏事务的一致性和数据库的一致性。所以数据库管理系统必须提供并发控制机制。</p>
<h1 id="并发控制概述"><a href="#并发控制概述" class="headerlink" title="并发控制概述"></a>并发控制概述</h1><p><strong>事务是并发控制的基本单位</strong>，保证</p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://xiaoqingming18.github.io/2023/05/31/%E6%95%B0%E6%8D%AE%E5%BA%93%E5%8E%9F%E7%90%86%EF%BC%9A%E6%95%B0%E6%8D%AE%E5%BA%93%E6%81%A2%E5%A4%8D%E6%8A%80%E6%9C%AF/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="鸣蜩十七">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="鸣蜩十七">
      <meta itemprop="description" content="记录我的学习、生活和恋爱日常">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 鸣蜩十七">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2023/05/31/%E6%95%B0%E6%8D%AE%E5%BA%93%E5%8E%9F%E7%90%86%EF%BC%9A%E6%95%B0%E6%8D%AE%E5%BA%93%E6%81%A2%E5%A4%8D%E6%8A%80%E6%9C%AF/" class="post-title-link" itemprop="url">数据库原理：数据库恢复技术</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>
      

      <time title="创建时间：2023-05-31 18:06:14 / 修改时间：20:11:32" itemprop="dateCreated datePublished" datetime="2023-05-31T18:06:14+08:00">2023-05-31</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E6%95%B0%E6%8D%AE%E5%BA%93%E5%8E%9F%E7%90%86/" itemprop="url" rel="index"><span itemprop="name">-数据库原理</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="事务"><a href="#事务" class="headerlink" title="事务"></a>事务</h1><h2 id="什么是事务"><a href="#什么是事务" class="headerlink" title="什么是事务"></a>什么是事务</h2><p><strong>事务</strong>是用户定义的一个数据库操作序列，这些操作要么全做，要么全不做，是一个不可分割的工作单位。</p>
<p>一个事务可以是一条或多条SQL语句，也可以是整个程序，但一般情况是一个程序中包含多个事务。</p>
<h2 id="定义事务"><a href="#定义事务" class="headerlink" title="定义事务"></a>定义事务</h2><p>在SQL中，定义事务的语句一般有三条：</p>
<ol>
<li>BEGIN TRANSACTION</li>
<li>COMMIT</li>
<li>ROLLBACK</li>
</ol>
<p>事务通常以<code>BEGIN TRANSACTION</code>开始，以<code>COMMIT</code>或<code>ROLLBACK</code>结束。其中，<code>COMMIT</code>表示提交，<strong>将事务中所有对数据库的更新写回到磁盘上的物理数据库中去</strong>，此时事务正常结束。<code>ROLLBACK</code>表示回滚，若在事务运行过程中发生意外，事务无法继续运行，<strong>系统便会将事务中所有已完成的操作全部撤回，回滚到事务开始时的状态</strong>。</p>
<h2 id="事务的ACID特性"><a href="#事务的ACID特性" class="headerlink" title="事务的ACID特性"></a>事务的ACID特性</h2><p>事务具有4个特性，分别是<strong>原子性、一致性、隔离性和持续性</strong>。这4个特性简称为<strong>ACID特性</strong>。下面详细解释一下这几个特性。</p>
<ol>
<li>原子性：事务是数据库的逻辑工作单位，事务中包括的所有操作，要么都做，要么都不做。</li>
<li>一致性：事务执行的结果是使数据库从一个一致性状态变为另一个一致性状态。当事务中所有的操作都做完或者都不做时，就是一个一致性状态。当事务执行过程中出现故障，这时数据库就不处于一致性状态了。</li>
<li>隔离性：一个事务的执行不能被其他事务干扰，及一个事务内部操作及使用的数据对其他并行操作的事务是隔离的，并发执行的各个事务之间不能互相干扰。</li>
<li>持续性：也成永久性，指一个事务一旦提交，它对数据库中数据的改变就应该是永久性的，接下来的其他操作或故障不应该对其执行结果有任何影响。</li>
</ol>
<p>事务ACID特性遭到破坏的原因可能有以下几个：</p>
<ol>
<li>多个事务并行运行时，不同事务的操作交叉执行；</li>
<li>事务在运行过程中被强行停止。</li>
</ol>
<p>在第一种情况下，<strong>数据库管理系统必须保证多个事务的交叉运行不影响这些事务的原子性</strong>；</p>
<p>在第二种情况下，<strong>数据库管理系统必须保证被强行终止的事务对数据库和其他事务没有任何影响</strong>。</p>
<p>以上所说的就是<strong>数据库管理系统中恢复机制和并发控制机制的责任</strong>。</p>
<h1 id="故障的种类"><a href="#故障的种类" class="headerlink" title="故障的种类"></a>故障的种类</h1><p>数据库系统中可能会发生各种各样的故障，大致可以分为以下几类</p>
<h2 id="事务内部的故障"><a href="#事务内部的故障" class="headerlink" title="事务内部的故障"></a>事务内部的故障</h2><p>事务内部的故障有的可以是通过事务程序本身发现并处理的，有的是非预期的，不能由事务处理。</p>
<p>先来看可以通过事务程序本身发现的故障，比如从一个账户向另一个账户赚钱，若当前帐户余额小于要转账的金额，便是一个故障，事务程序应当发现并进行回滚（<code>ROLLBACK</code>）操作。</p>
<p><strong>事务内部更多的故障是非预期的，是不能由应用程序处理的。</strong></p>
<p>事务故障意味着事务没有达到预期的终点（<code>COMMIT</code>或显式的<code>ROLLBACK</code>），恢复程序要在不影响其他事务运行的情况下，强行回滚该事务，即<strong>撤销该事务已经作出的任何对数据库的修改</strong>。这类恢复操作称为<strong>事务撤销（UNDO）</strong>。</p>
<h2 id="系统故障"><a href="#系统故障" class="headerlink" title="系统故障"></a>系统故障</h2><p><strong>系统故障是指造成系统停止运转的任何事件，使得系统要重新启动</strong>。（例如硬件错误、操作系统故障和系统断电等）。<strong>这类故障影响正在运行的所有事务，但不破坏数据库</strong>。</p>
<p>系统故障造成数据库不一致状态的原因有两个，一个是未完成事务对数据库的更新可能已写入数据库，二是已提交事务对数据库的更新可能还留在缓冲区没来得及写入数据库。</p>
<p>恢复子系统必须在系统重新启动时让所有非正常结束的事务回滚，强行撤销所有未完成的事务。</p>
<p>系统重新启动后，恢复子系统还需要<strong>重做（REDO）</strong>所有已完成的事务，以将数据库真正恢复到一致状态。</p>
<h2 id="介质故障"><a href="#介质故障" class="headerlink" title="介质故障"></a>介质故障</h2><p>系统故障称为<strong>软故障</strong>，介质故障称为<strong>硬故障</strong>。硬故障指外在故障，如磁盘损坏、磁头碰撞和瞬时强磁场干扰等。这类故障将破坏数据库或部分数据库。</p>
<h2 id="计算机病毒"><a href="#计算机病毒" class="headerlink" title="计算机病毒"></a>计算机病毒</h2><p>计算机病毒是一种人为故障或破坏。</p>
<h2 id="故障总结"><a href="#故障总结" class="headerlink" title="故障总结"></a>故障总结</h2><p><strong>各类故障对数据库的影响有以下两种可能性</strong>：</p>
<ol>
<li>数据库本身被破坏；</li>
<li>数据库本身没被破坏，但数据可能不正确。</li>
</ol>
<h1 id="恢复的实现技术"><a href="#恢复的实现技术" class="headerlink" title="恢复的实现技术"></a>恢复的实现技术</h1><p>恢复的基本原理很简单，用一个词概括就是<strong>冗余</strong>，数据库中任何一部分被破坏或不正确的数据可以根据存储在系统别处的冗余数据来重建。</p>
<p><strong>建立冗余数据最常用的技术就是数据转储和登记日志文件（logging）。</strong>在一个数据库系统中，这两种方法通常是一起使用的。</p>
<h2 id="数据转储"><a href="#数据转储" class="headerlink" title="数据转储"></a>数据转储</h2><p>数据转储是数据库恢复中采用的基本技术。转储即<strong>数据库管理员定期地将整个数据库复制到磁带、磁盘或其他存储介质上保存起来的过程</strong>。这些备用的数据称为<strong>后备副本</strong>或<strong>后援副本</strong>。</p>
<p>当数据库遭到破坏后将后备副本重新装入，<strong>重装后备副本只能将数据库恢复到转储时的状态</strong>，要想恢复到故障发生时的状态，还需要运行自转储成功后的所有更新事务。</p>
<p>转储可分为静态转储和动态转储</p>
<h3 id="静态转储"><a href="#静态转储" class="headerlink" title="静态转储"></a>静态转储</h3><p><strong>静态转储是指在系统中没有运行事务时进行的转储操作</strong>，此时转储得到的一定是一个数据一致性的副本。静态转储简单，但转储必须等待正在运行的用户事务结束后才能进行。同样，新的事务必须等待转储结束后才能执行。显然，<strong>静态转储会降低数据库的可用性</strong>。</p>
<h3 id="动态转储"><a href="#动态转储" class="headerlink" title="动态转储"></a>动态转储</h3><p><strong>动态转储是指在转储期间允许对数据库进行存取或修改</strong>。转储结束后得到的后援副本上的数据并不能保证正确有效。比如，在转储期间的某个时刻$T_c$，系统把数据$A=10$转储到磁带上，而在下一时刻$T_d$，某一事务将A改为200.转储结束后，后备副本上的A已经是过时的数据了。</p>
<p>因此，必须把转储期间各事务对数据库的修改活动登记下来，建立<strong>日志文件</strong>。这样，<strong>后备副本加上日志文件就能把数据库恢复到某一时刻的正确状态</strong>。</p>
<h3 id="海量转储与增量转储"><a href="#海量转储与增量转储" class="headerlink" title="海量转储与增量转储"></a>海量转储与增量转储</h3><p>转储还可以分为海量转储和增量转储两种。海量转储是指每次转储全部数据库，增量转储是指每次只转储上次转储后更新的数据。</p>
<h2 id="登记日志文件"><a href="#登记日志文件" class="headerlink" title="登记日志文件"></a>登记日志文件</h2><h3 id="日志文件的格式和内容"><a href="#日志文件的格式和内容" class="headerlink" title="日志文件的格式和内容"></a>日志文件的格式和内容</h3><p><strong>日志文件是用来记录事务对数据库的更新操作的文件</strong>。日志分为以记录为单位的日志文件和以数据块为单位的日志文件。</p>
<p><strong>以记录为单位的日志文件中需要登记的内容包括：</strong></p>
<ul>
<li>各个事务的开始（<code>BEGIN TRANSACTION</code>）标记；</li>
<li>各个事务的结束（<code>COMMIT</code>或<code>ROOLBACK</code>）标记；</li>
<li>各个事务的所有更新操作。</li>
</ul>
<p>这里每个事务的开始标记、结束标记和更新操作都是日志文件中的一个日志记录。</p>
<p>日志记录的内容主要包括：</p>
<ul>
<li>事务标识（标明是哪个事务）；</li>
<li>操作的类型（插入、删除或修改）；</li>
<li>操作对象（记录内部标识）；</li>
<li>更新前数据的旧值（对插入操作而言，此项为空）；</li>
<li>更喜欢后数据的新值（对删除操作而言，此项为空）。</li>
</ul>
<p><strong>以数据块为单位的日志文件，日志记录的内容包括事务标识和被更新的数据块。</strong>因为将更新前和更新后的整个块都放入日志文件中，操作类型和操作对象等信息都不需要放入日志记录中了。</p>
<h3 id="日志文件的作用"><a href="#日志文件的作用" class="headerlink" title="日志文件的作用"></a>日志文件的作用</h3><p>日志文件在数据库恢复中起着非常重要的作用，<strong>可以用来进行事务故障恢复和系统故障恢复</strong>，并协助后备副本进行截止故障恢复，具体作用如下：</p>
<ol>
<li>事务故障恢复和系统故障恢复必须用日志文件；</li>
<li>在动态转储方式中必须建立日志文件，后备副本和日志文件结合起来才能有效地恢复数据库；</li>
<li>在静态转储方式中也可以建立日志文件，数据库破坏后可以重新装入后备副本把数据库恢复到转储结束时刻的状态，然后利用日志文件把转储后已完成的事务进行重做处理，对故障发生时尚未完成的事务进行撤销处理。这样不必运行那些已完成的事务程序就可以把数据库恢复到故障前某一时刻的正确状态，如下图所示。</li>
</ol>
<p><img src="/2023/05/31/%E6%95%B0%E6%8D%AE%E5%BA%93%E5%8E%9F%E7%90%86%EF%BC%9A%E6%95%B0%E6%8D%AE%E5%BA%93%E6%81%A2%E5%A4%8D%E6%8A%80%E6%9C%AF/利用日志文件恢复.png" alt="利用日志文件恢复"></p>
<h3 id="登记日志文件-1"><a href="#登记日志文件-1" class="headerlink" title="登记日志文件"></a>登记日志文件</h3><p>为保证数据库是可恢复的，登记日志文件时必须遵循两条原则：</p>
<ol>
<li>登记的次序严格按照并发事务执行的时间次序。</li>
<li>必须先写日志文件，后写数据库。</li>
</ol>
<h1 id="恢复策略"><a href="#恢复策略" class="headerlink" title="恢复策略"></a>恢复策略</h1><p>当系统运行过程中发生故障，利用数据库后备副本和日志文件就可以将数据库恢复到故障前的某个一致性状态。不同故障其恢复策略和方法也不一样。</p>
<h2 id="事务故障的恢复"><a href="#事务故障的恢复" class="headerlink" title="事务故障的恢复"></a>事务故障的恢复</h2><p>事务故障是事务在运行至终点前被终止，这时恢复子系统应该利用日志文件撤销（UNDO）此事务已对数据库进行的修改。事务故障的恢复是由系统自动完成的，对用户是透明的。</p>
<p>系统的恢复步骤如下：</p>
<ol>
<li>反向扫描日志文件，查找该事务的更新操作。</li>
<li>对该事务的更新操作执行逆操作。</li>
<li>继续反向扫描日志文件，查找该事务的其他更新操作，并做同样处理。</li>
<li>如此处理下去，直至读到此事务的开始标记，事务故障恢复就完成了。</li>
</ol>
<h2 id="系统故障的恢复"><a href="#系统故障的恢复" class="headerlink" title="系统故障的恢复"></a>系统故障的恢复</h2><p>前面已经讲过，系统故障造成数据库不一致状态的原因有两个，一个是未完成事务对数据库的更新可能已写入数据库，二是已提交事务对数据库的更新可能还留在缓冲区没来得及写入数据库。因此，<strong>恢复操作就是要撤销故障发生时未完成的事务，重做已完成的事务</strong>。</p>
<p>系统的恢复步骤如下：</p>
<ol>
<li><p>正向扫描日志文件，找出在故障发生前已经提交的事务，将其事务标识记入重做队列。同时找出故障发生时尚未完成的事务，将其事务标识记入撤销队列。</p>
</li>
<li><p>对撤销队列中的各个事务进行撤销（UNDO）处理。</p>
<p>进行撤销的方法是，反向扫描日志文件，对每个撤销事务的更新操作执行逆操作。</p>
</li>
<li><p>对重做队列中的各个事务进行重做处理。</p>
<p>进行重做处理的方法是：正向扫描日志文件，对每个重做事务重新执行日志文件登记的操作。</p>
</li>
</ol>
<h2 id="介质故障的恢复"><a href="#介质故障的恢复" class="headerlink" title="介质故障的恢复"></a>介质故障的恢复</h2><p>发生介质故障后，磁盘上的物理数据和日志文件被破坏，这是最严重的一种故障，恢复方法是<strong>重装数据库，然后重做已完成的事务</strong>。</p>
<p>具体的恢复步骤如下：</p>
<ol>
<li><p>装入最新的后备副本，使数据库恢复到最近一次转储时的一致性状态。</p>
<p>对于动态转储的数据库副本，还需同时装入转储开始时刻的日志文件副本，利用恢复系统故障的方法（REDO+UNDO），才能将数据库恢复到一致性状态。</p>
</li>
<li><p>装入相应的日志文件副本（转储结束时刻的日志文件副本），重做已完成的事务。</p>
</li>
</ol>
<p>这样就可以将数据库恢复到故障前某一时刻的一致状态了。</p>
<h1 id="具有检查点的恢复技术"><a href="#具有检查点的恢复技术" class="headerlink" title="具有检查点的恢复技术"></a>具有检查点的恢复技术</h1><p>利用日志文件恢复数据库存在一些弊端，比如有些已完成的事务已经将数据写入数据库了，但恢复子系统还是要重做这些事务，并且搜索整个日志文件需要大量的时间。</p>
<p>为了解决上面所说的问题，有发展了<strong>具有检查点的恢复技术</strong>。这种技术在日志文件中增加一类新的记录——<strong>检查点</strong>记录，增加一个重新开始文件，并让恢复子系统在登录日志文件期间动态地维护日志。</p>
<h2 id="检查点记录的内容"><a href="#检查点记录的内容" class="headerlink" title="检查点记录的内容"></a>检查点记录的内容</h2><p>检查点记录的内容包括：</p>
<ul>
<li>建立检查点时刻所有正在执行的事务清单。</li>
<li>这些事务最近一个日志记录的地址</li>
</ul>
<p>重新开始文件用来记录各个检查点在日志文件中的地址，下图说明了建立检查点$C_i$时对应的日志文件和重新开始文件。</p>
<p><img src="/2023/05/31/%E6%95%B0%E6%8D%AE%E5%BA%93%E5%8E%9F%E7%90%86%EF%BC%9A%E6%95%B0%E6%8D%AE%E5%BA%93%E6%81%A2%E5%A4%8D%E6%8A%80%E6%9C%AF/具有检查点的日志文件和重新开始文件.png" alt="具有检查点的日志文件和重新开始文件"></p>
<h2 id="动态维护日志的方法"><a href="#动态维护日志的方法" class="headerlink" title="动态维护日志的方法"></a>动态维护日志的方法</h2><p>动态维护日志的方法是，<strong>周期性地执行建立检查点、保存数据库状态的操作</strong>。具体步骤如下：</p>
<ol>
<li>将当前日志缓冲区的所有日志记录写入磁盘的日志文件上。</li>
<li>在日志文件中写入一个检查点记录。</li>
<li>将当前数据缓冲区的所有数据记录写入磁盘的数据库中。</li>
<li>把检查点记录在日志文件中的地址写入一个重新开始文件。</li>
</ol>
<p>恢复子系统可以定期或不定期地建立检查点，保存数据库状态。检查点可以按照预定的一个时间间隔建立，也可以按照某种规则建立检查点。</p>
<p><strong>使用检查点放大可以改善恢复效率</strong>。当事务T在一个检查点之前被提交，T对数据库做的修改一定都已经被写入数据库，写入时间是在这个检查点建立之前或者在这个检查点建立之时。这样，再进行恢复处理时，没有必要对事务T执行重做操作。</p>
<p>系统出现故障时，恢复子系统根据事务的不同状态采取不同的恢复策略，如下图所示：</p>
<p><img src="/2023/05/31/%E6%95%B0%E6%8D%AE%E5%BA%93%E5%8E%9F%E7%90%86%EF%BC%9A%E6%95%B0%E6%8D%AE%E5%BA%93%E6%81%A2%E5%A4%8D%E6%8A%80%E6%9C%AF/恢复子系统采取的不同策略.png" alt="恢复子系统采取的不同策略"></p>
<p>其中各时刻代表意义如下：</p>
<ul>
<li>$T_1$：在检查点之前提交——不要重做。</li>
<li>$T_2$：在检查点之前开始执行，在检查点之后故障点之前提交——重做。</li>
<li>$T_3$：在检查点之前开始执行，在故障点时还未完成——撤销。</li>
<li>$T_4$：在检查点之后开始执行，在故障点之前提交——重做。</li>
<li>$T_5$：在检查点之后开始执行，在故障点时还未完成——撤销。</li>
</ul>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://xiaoqingming18.github.io/2023/05/24/Java%EF%BC%9A%E8%BE%93%E5%85%A5%E8%BE%93%E5%87%BA%E6%B5%81/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="鸣蜩十七">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="鸣蜩十七">
      <meta itemprop="description" content="记录我的学习、生活和恋爱日常">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 鸣蜩十七">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2023/05/24/Java%EF%BC%9A%E8%BE%93%E5%85%A5%E8%BE%93%E5%87%BA%E6%B5%81/" class="post-title-link" itemprop="url">Java：输入输出流</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2023-05-24 22:24:23" itemprop="dateCreated datePublished" datetime="2023-05-24T22:24:23+08:00">2023-05-24</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-05-25 00:18:39" itemprop="dateModified" datetime="2023-05-25T00:18:39+08:00">2023-05-25</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%90%8E%E7%AB%AF/" itemprop="url" rel="index"><span itemprop="name">-后端</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="输入输出流介绍"><a href="#输入输出流介绍" class="headerlink" title="输入输出流介绍"></a>输入输出流介绍</h1><h2 id="输入流"><a href="#输入流" class="headerlink" title="输入流"></a>输入流</h2><p>输入流分为<strong>字节输入流</strong><code>InputStream</code>和<strong>字符输入流</strong><code>Reader</code>，它们都是抽象类。下图给出了<code>InputStream</code>类和<code>Reader</code>类的层次结构。</p>
<h2 id="输出流"><a href="#输出流" class="headerlink" title="输出流"></a>输出流</h2><p>输出流分为<strong>字节输出流</strong><code>OutputStream</code>和<strong>字符输出流</strong><code>Writer</code>，它们也都是抽象类。下图给出了<code>OutputStream</code>类和<code>Writer</code>类的层次结构。</p>
<h1 id="输入流-1"><a href="#输入流-1" class="headerlink" title="输入流"></a>输入流</h1><h2 id="InputStream类"><a href="#InputStream类" class="headerlink" title="InputStream类"></a>InputStream类</h2><p>下面是<code>InputStream</code>类的一些常用方法和说明：</p>
<div class="table-container">
<table>
<thead>
<tr>
<th>方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>abstract int read()</td>
<td>从数据中读入一个字节，返回一个字节的数据（0-255的整数值），如果已经读取到流的末尾，返回 -1 表示没有更多的数据可读取。</td>
</tr>
<tr>
<td>int read(byte[] b)</td>
<td>返回实际读取的字节数，将读取的数据存储到给定的字节数组 <code>b</code> 中，读取的实际字节数为<code>b.length</code>。</td>
</tr>
<tr>
<td>int read(byte[] b,int off,int len)</td>
<td>返回实际读取的字节数，将读取的数据存储到给定的字节数组 <code>b</code> 的指定偏移量 <code>off</code> 开始的位置，并最多读取 <code>len</code> 个字节的数据。</td>
</tr>
<tr>
<td>byte[] readAllBytes()</td>
<td>返回一个数组，包含可以从当前流中读入的所有字节</td>
</tr>
<tr>
<td>long transferTo(OutputStream)</td>
<td>将当前输入流中的所有字节传送到给定的输出流，返回传递的字节数。<strong>这两个流都不能处于关闭状态</strong>。</td>
</tr>
<tr>
<td>long skip(long n)</td>
<td>在输入流中跳过n个字节，返回实际跳过的字节数（如果碰到数据流结尾，可能小于0）。</td>
</tr>
<tr>
<td>int available()</td>
<td>返回在不阻塞情况下可以读取的字节数。</td>
</tr>
<tr>
<td>void close()</td>
<td>关闭这个输入流。</td>
</tr>
<tr>
<td>void mark(int readLimit)</td>
<td>在输入流的当前位置打一个标记（并非所有的流都支持此特性），如果从输入流中已经读取的字节数多于<code>readLimit</code>个，则这个流允许忽略这个标记。</td>
</tr>
<tr>
<td>void reset()</td>
<td>返回到最后一个标记，随后对read的调用将重新读入这些字节。如果当前流没有任何标记，则这个流不被重置。</td>
</tr>
<tr>
<td>boolean markSupported()</td>
<td>如果这个流支持打标记，则返回<code>true</code>。</td>
</tr>
</tbody>
</table>
</div>
<p>要注意的是，<code>read()</code> 方法是一个抽象方法。抽象方法是在抽象类中声明但没有具体实现的方法，因此无法直接实例化抽象类。但可以使用 <code>InputStream</code> 类的具体子类来创建对象，并使用子类中实现的 <code>read()</code> 方法。这些子类是具体的输入流实现，例如 <code>FileInputStream</code>、<code>ByteArrayInputStream</code> 等。这些具体子类继承自 <code>InputStream</code> 并提供了对输入源的具体读取操作。</p>
<p>另外，上述最后 3 个方法一般会结合在一起使用完成<strong>重复读取操作</strong>，首先使用 markSupported() 判断当前流是否支持标记，如果可以，则使用 mark(int readLimit) 方法进行标记，标记完成之后可以使用 read() 方法读取标记范围内的字节数，最后使用 reset() 方法使输入流重新定位到标记的位置，继而完成重复读取操作。看下面的例子：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.io.FileInputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">import</span> java.io.InputStream;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RepeatReadExample</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">        <span class="keyword">try</span> {</span><br><span class="line">            <span class="type">InputStream</span> <span class="variable">inputStream</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileInputStream</span>(<span class="string">"file.txt"</span>);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 设置标记，读取的字节数限制为10</span></span><br><span class="line">            inputStream.mark(<span class="number">10</span>);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 第一次读取</span></span><br><span class="line">            System.out.println(<span class="string">"First read:"</span>);</span><br><span class="line">            readData(inputStream, <span class="number">5</span>);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 重置输入流，返回到标记位置</span></span><br><span class="line">            inputStream.reset();</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 第二次读取</span></span><br><span class="line">            System.out.println(<span class="string">"Second read:"</span>);</span><br><span class="line">            readData(inputStream, <span class="number">5</span>);</span><br><span class="line"></span><br><span class="line">            inputStream.close();</span><br><span class="line">        } <span class="keyword">catch</span> (IOException e) {</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">readData</span><span class="params">(InputStream inputStream, <span class="type">int</span> numBytes)</span> <span class="keyword">throws</span> IOException {</span><br><span class="line">        <span class="type">byte</span>[] buffer = <span class="keyword">new</span> <span class="title class_">byte</span>[numBytes];</span><br><span class="line">        <span class="type">int</span> <span class="variable">bytesRead</span> <span class="operator">=</span> inputStream.read(buffer);</span><br><span class="line">        <span class="keyword">if</span> (bytesRead != -<span class="number">1</span>) {</span><br><span class="line">            System.out.println(<span class="string">"Read: "</span> + <span class="keyword">new</span> <span class="title class_">String</span>(buffer, <span class="number">0</span>, bytesRead));</span><br><span class="line">        } <span class="keyword">else</span> {</span><br><span class="line">            System.out.println(<span class="string">"End of stream reached."</span>);</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure>
<h3 id="Reader类"><a href="#Reader类" class="headerlink" title="Reader类"></a>Reader类</h3><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">AnalysisResult</span><br></pre></td></tr></tbody></table></figure>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://xiaoqingming18.github.io/2023/05/21/Java%EF%BC%9AIO%E6%B5%81/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="鸣蜩十七">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="鸣蜩十七">
      <meta itemprop="description" content="记录我的学习、生活和恋爱日常">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 鸣蜩十七">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2023/05/21/Java%EF%BC%9AIO%E6%B5%81/" class="post-title-link" itemprop="url">Java：IO流</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>
      

      <time title="创建时间：2023-05-21 20:45:34 / 修改时间：21:01:29" itemprop="dateCreated datePublished" datetime="2023-05-21T20:45:34+08:00">2023-05-21</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%90%8E%E7%AB%AF/" itemprop="url" rel="index"><span itemprop="name">-后端</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="输入-输出流"><a href="#输入-输出流" class="headerlink" title="输入/输出流"></a>输入/输出流</h1><p><strong>输入流</strong>是可以从中读入一个字节序列的对象，<strong>输出流</strong>是可以向其中写入一个字节序列的对象。这些字节序列通常是文件，也可以是网络连接，甚至是内存块。</p>
<p>抽象类<code>InputStream</code>和<code>OutputStream</code>构成了输入/输出（I/O）类层次结构的基础。</p>
<p>面向字节的流不便处理以Unicode形式存储的信息，所以从抽象类<code>Reader</code>和<code>Writer</code>中继承出来了一个专门用来处理Unicode字符的单独的类层次结构。这些类拥有的读入和写出操作都是基于两字节的<code>Char</code>值的（即Unicode码元），而不是基于<code>byte</code>值的。</p>
<h2 id="读写字节"><a href="#读写字节" class="headerlink" title="读写字节"></a>读写字节</h2><p><code>InputStream</code>类有一个抽象方法：<code>abstract int read()</code></p>
<p>这个方法将读入一个字节，并返回读入的字节，若遇到输入源结尾，则返回-1.</p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://xiaoqingming18.github.io/2023/05/20/Java%EF%BC%9AGUI%E7%BC%96%E7%A8%8B/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="鸣蜩十七">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="鸣蜩十七">
      <meta itemprop="description" content="记录我的学习、生活和恋爱日常">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 鸣蜩十七">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2023/05/20/Java%EF%BC%9AGUI%E7%BC%96%E7%A8%8B/" class="post-title-link" itemprop="url">Java：GUI编程</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2023-05-20 10:52:23" itemprop="dateCreated datePublished" datetime="2023-05-20T10:52:23+08:00">2023-05-20</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-06-29 07:59:30" itemprop="dateModified" datetime="2023-06-29T07:59:30+08:00">2023-06-29</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%90%8E%E7%AB%AF/" itemprop="url" rel="index"><span itemprop="name">-后端</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="GUI框架介绍"><a href="#GUI框架介绍" class="headerlink" title="GUI框架介绍"></a>GUI框架介绍</h1><p>Java GUI 总体框架包括两个主要的部分：AWT（Abstract Window Toolkit）和 Swing。AWT 是 Java 原生的 GUI 库，而 Swing 是在 AWT 的基础上开发的一个更高级的 GUI 库。下面分别介绍一下 AWT 和 Swing 的特点和各自负责的部分。</p>
<h2 id="AWT"><a href="#AWT" class="headerlink" title="AWT"></a>AWT</h2><p>AWT 是 Java 最早的 GUI 库，它提供了一套基本的 GUI 组件，如窗口、按钮、文本框、复选框、列表框等。这些组件是使用本地窗口系统（如 Windows、Mac OS X 或 Linux）提供的原生控件实现的，因此<strong>AWT应用程序在不同的平台上看起来会有些许不同</strong>。AWT 是 Java 标准库的一部分，因此它可以在任何支持 Java 的平台上运行。</p>
<p>在 AWT 中，组件都是继承自 java.awt.Component 类的。AWT 的布局管理器（LayoutManager）负责组件的布局和排列，常用的布局管理器包括 BorderLayout、FlowLayout、GridLayout 等。</p>
<h2 id="Swing"><a href="#Swing" class="headerlink" title="Swing"></a>Swing</h2><p>Swing 是在 AWT 的基础上开发的一个更高级的 GUI 库，它提供了一套丰富的 GUI 组件和更强大的功能，如表格、树形控件、滚动条等。Swing 的组件是使用 Java 代码实现的，而不是使用本地窗口系统提供的原生控件，因此 <strong>Swing 应用程序在不同的平台上看起来完全一样</strong>。Swing 是 Java 的标准库之一，因此它可以在任何支持 Java 的平台上运行。</p>
<p>Swing 的主要特点包括：</p>
<ul>
<li>跨平台性：Swing 应用程序可以在任何支持 Java 的平台上运行，因为它使用了 Java 代码实现的 GUI 组件，而不是本地窗口系统提供的原生控件。</li>
<li>功能丰富：Swing 提供了一套丰富的 GUI 组件和更强大的功能，可以满足复杂 GUI 应用程序的需求。</li>
<li>可定制性：Swing 组件可以通过修改外观和行为来实现定制化，如修改组件的背景颜色、字体、样式等。</li>
<li>性能较高：Swing 的性能相对较高，因为它使用了 Java 代码实现的 GUI 组件，可以在处理复杂界面时提供更好的性能。</li>
<li>复杂度高：Swing 的开发和使用相对 AWT 来说更加复杂，因为它提供了更多的功能和可定制性，需要更多的学习和实践才能掌握。</li>
</ul>
<p>在 Swing 中，组件都是继承自 javax.swing.JComponent 类的。Swing 的布局管理器和 AWT 类似，也负责组件的布局和排列，常用的布局管理器包括 BorderLayout、FlowLayout、GridLayout、GridBagLayout 等。除此之外，Swing 还提供了更高级的布局管理器，如 BoxLayout、CardLayout等。</p>
<h2 id="Swing框架图"><a href="#Swing框架图" class="headerlink" title="Swing框架图"></a>Swing框架图</h2><p>下面我们通过一张图来介绍了解一下Swing有哪些组件：</p>
<p><img src="/2023/05/20/Java%EF%BC%9AGUI%E7%BC%96%E7%A8%8B/框架图.png" alt="框架图"></p>
<h1 id="容器与组件"><a href="#容器与组件" class="headerlink" title="容器与组件"></a>容器与组件</h1><h2 id="容器"><a href="#容器" class="headerlink" title="容器"></a>容器</h2><p>在 Java GUI 中，容器是一个可以包含其他组件的区域，它可以是顶级容器（如 JFrame、JDialog 等），也可以是内部容器（如 JPanel、JScrollPane 等）。容器可以嵌套，也就是说一个容器可以包含另一个容器，从而形成复杂的 GUI 布局。</p>
<p>常用的容器有：</p>
<ul>
<li>JFrame：顶级容器，代表一个窗口。</li>
<li>JDialog：顶级容器，代表一个对话框。</li>
<li>JPanel：内部容器，可以用来组织其他组件。</li>
<li>JScrollPane：内部容器，可以包含一个可滚动的视图。</li>
<li>JTabbedPane：内部容器，可以包含多个选项卡，每个选项卡可以显示不同的内容。</li>
</ul>
<h2 id="组件"><a href="#组件" class="headerlink" title="组件"></a>组件</h2><p>在 Java GUI 中，组件是一个可以在容器中显示的 GUI 元素，它可以是按钮、文本框、标签、列表框、滚动条等。组件可以接收用户的输入，也可以显示程序的输出。</p>
<h2 id="向容器中添加组件"><a href="#向容器中添加组件" class="headerlink" title="向容器中添加组件"></a>向容器中添加组件</h2><p>可以通过容器的<code>add(Component c)</code>方法向容器中添加组件，看下面的例子：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">JPanel</span> <span class="variable">panel</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JPanel</span>(); <span class="comment">// 创建一个 JPanel</span></span><br><span class="line"><span class="type">JButton</span> <span class="variable">button</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JButton</span>(<span class="string">"Click Me"</span>); <span class="comment">// 创建一个 JButton</span></span><br><span class="line">panel.add(button); <span class="comment">// 将 JButton 添加到 JPanel 中</span></span><br></pre></td></tr></tbody></table></figure>
<h1 id="窗口"><a href="#窗口" class="headerlink" title="窗口"></a>窗口</h1><p>窗口是一个重要的概念，它是用户与程序交互的主要方式之一。Java 提供了多种窗口组件，如<code>JFrame</code>、<code>JDialog</code>、<code>JOptionPane</code> 等。这里先介绍<code>JFrame</code>的窗口操作。</p>
<h2 id="JFrame"><a href="#JFrame" class="headerlink" title="JFrame"></a>JFrame</h2><p>JFrame 是 Java GUI 中最常用的窗口组件，它代表一个顶级窗口，用于显示 GUI 应用程序的主界面。</p>
<p>JFrame 的构造方法如下：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">JFrame() <span class="comment">// 创建一个默认的 JFrame，其标题为 ""</span></span><br><span class="line">JFrame(String title) <span class="comment">// 创建一个指定标题的 JFrame</span></span><br></pre></td></tr></tbody></table></figure>
<p>JFrame 的常用方法如下：</p>
<ul>
<li>setTitle(String title)：设置 JFrame 的标题。</li>
<li>setSize(int width, int height)：设置 JFrame 的大小。</li>
<li>setLocation(int x, int y)：设置 JFrame 的位置。</li>
<li>setResizable(boolean resizable)：设置 JFrame 是否可调整大小，默认可调整大小。</li>
<li>add(Component c)：向 JFrame 中添加一个组件。</li>
<li>setVisible(boolean visible)：显示或隐藏 JFrame，<strong>默认是隐藏的，创建好窗口需要手动设置窗口为可见</strong>。</li>
<li>getContentPane()：返回 JFrame 的内容面板，可以向内容面板中添加其他组件。</li>
<li>setJMenuBar(JMenuBar menuBar)：设置 JFrame 的菜单栏。</li>
<li>setLayout(LayoutManager manager)：设置 JFrame 的布局管理器。</li>
<li>setIconImage(Image image)：设置 JFrame 的图标。</li>
<li>setDefaultCloseOperation(int operation)：用于设置窗口关闭时的操作。该方法接受一个整数参数，用于指定关闭时的操作，常用的参数值包括：（<strong>默认为HIDE_ON_CLOSE，即隐藏窗口</strong>）<ul>
<li>EXIT_ON_CLOSE：退出程序。当用户关闭窗口时，程序将终止。</li>
<li>DISPOSE_ON_CLOSE：释放资源。当用户关闭窗口时，窗口将被销毁，但程序将继续运行。</li>
<li>DO_NOTHING_ON_CLOSE：什么都不做。当用户关闭窗口时，不执行任何操作。</li>
</ul>
</li>
</ul>
<h2 id="菜单条、菜单、菜单项"><a href="#菜单条、菜单、菜单项" class="headerlink" title="菜单条、菜单、菜单项"></a>菜单条、菜单、菜单项</h2><p>菜单条、菜单、菜单项是窗口常用的组件，菜单放在菜单条里，菜单项放在菜单里。下面分别介绍一下这些组件的特点和用法。</p>
<h3 id="JMenuBar"><a href="#JMenuBar" class="headerlink" title="JMenuBar"></a>JMenuBar</h3><p>JMenuBar 是 Java GUI 中的菜单条组件，用于显示菜单和子菜单。它通常位于窗口的顶部，可以包含多个 JMenu 组件。</p>
<p>JMenuBar 的用法如下：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">JMenuBar</span> <span class="variable">menuBar</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JMenuBar</span>(); <span class="comment">// 创建一个 JMenuBar</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 创建一个 JMenu，并添加到 JMenuBar 中</span></span><br><span class="line"><span class="type">JMenu</span> <span class="variable">fileMenu</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JMenu</span>(<span class="string">"File"</span>);</span><br><span class="line">menuBar.add(fileMenu);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 创建一个 JMenuItem，并添加到 JMenu 中</span></span><br><span class="line"><span class="type">JMenuItem</span> <span class="variable">openItem</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JMenuItem</span>(<span class="string">"Open"</span>);</span><br><span class="line">fileMenu.add(openItem);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 将 JMenuBar 设置为 JFrame 的菜单栏</span></span><br><span class="line">frame.setJMenuBar(menuBar);</span><br></pre></td></tr></tbody></table></figure>
<h3 id="JMenu"><a href="#JMenu" class="headerlink" title="JMenu"></a>JMenu</h3><p>JMenu 是 Java GUI 中的菜单组件，用于显示菜单和子菜单。它通常作为 JMenuBar 的子组件，可以包含多个 JMenuItem 组件。用法如下：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">JMenu</span> <span class="variable">fileMenu</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JMenu</span>(<span class="string">"File"</span>); <span class="comment">// 创建一个 JMenu</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 创建多个 JMenuItem，并添加到 JMenu 中</span></span><br><span class="line"><span class="type">JMenuItem</span> <span class="variable">openItem</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JMenuItem</span>(<span class="string">"Open"</span>);</span><br><span class="line"><span class="type">JMenuItem</span> <span class="variable">saveItem</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JMenuItem</span>(<span class="string">"Save"</span>);</span><br><span class="line">fileMenu.add(openItem);</span><br><span class="line">fileMenu.add(saveItem);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 将 JMenu 添加到 JMenuBar 中</span></span><br><span class="line">menuBar.add(fileMenu);</span><br></pre></td></tr></tbody></table></figure>
<p>以上代码创建了一个 JMenu，并向其中添加了多个 JMenuItem。最后将 JMenu 添加到 JMenuBar 中。</p>
<h3 id="JMenuItem"><a href="#JMenuItem" class="headerlink" title="JMenuItem"></a>JMenuItem</h3><p>JMenuItem 是 Java GUI 中的菜单项组件，用于表示菜单和子菜单中的一个选项。它通常作为 JMenu 的子组件，可以响应用户的鼠标单击事件。看下面的例子：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">JMenuItem</span> <span class="variable">openItem</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JMenuItem</span>(<span class="string">"Open"</span>); <span class="comment">// 创建一个 JMenuItem</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 将 JMenuItem 添加到 JMenu 中</span></span><br><span class="line">fileMenu.add(openItem);</span><br></pre></td></tr></tbody></table></figure>
<h1 id="常用组件"><a href="#常用组件" class="headerlink" title="常用组件"></a>常用组件</h1><p>常用组件都是<code>JComponent</code>的子类。</p>
<ol>
<li><code>JTextField</code>：文本框，允许用户在文本框中输入<strong>单行文本</strong>。</li>
<li><code>JTextArea</code>：文本区，允许用户在文本区输入<strong>多行文本</strong>。</li>
<li><code>JButton</code>：按钮，允许用户单击按钮。</li>
<li><code>JLabel</code>：标签，为用户提供提示信息。</li>
<li><code>JCheckBox</code>：复选框，为用户提供<strong>多项选择</strong>。</li>
<li><code>JRadioButton</code>：单选按钮，为用户提供<strong>单项选择</strong>。（如何实现单选）</li>
<li><code>JComboBox</code>：下拉列表，为用户提供下拉列表形式的<strong>单项选择</strong>。（菱形语法选择Item类型，使用addItem加入元素）</li>
<li><code>JPasswordField</code>：密码框，允许用户在密码框中输入单行密码，密码的默认回显字符是’ * ‘，可以使用<code>setEchoChar(char c)</code>方法设置回显字符，用户还可以通过<code>char[] getPassword()</code>方法获取用户输入的密码。</li>
</ol>
<h1 id="常用容器"><a href="#常用容器" class="headerlink" title="常用容器"></a>常用容器</h1><p><code>JComponent</code>是<code>Container</code>的子类，因此<code>JComponent</code>子类创建的组件也是容器，但是很少将组件当做容器来使用。<code>JComponent</code>提供了一些专门用来做容器的组件，相对于底层容器<code>JFrame</code>，本节提到的容器习惯地被称为<strong>中间容器</strong>，中间容器必须被添加到底层容器中才能发挥作用。</p>
<h2 id="JPanel（面板）"><a href="#JPanel（面板）" class="headerlink" title="JPanel（面板）"></a>JPanel（面板）</h2><p><code>JPanel</code>可以包含其他GUI组件，如按钮、文本框、标签等它不具备窗口装饰和其他窗口特性，因此常常被用作其他容器（如 JFrame、JDialog）中的子容器，用于布置和组织其他组件。JPanel 的用法非常简单，只需要创建一个 JPanel 对象，然后向其中添加其他组件即可。看下面的例子：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> javax.swing.*;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyPanel</span> <span class="keyword">extends</span> <span class="title class_">JPanel</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">MyPanel</span><span class="params">()</span> {</span><br><span class="line">        <span class="type">JButton</span> <span class="variable">button1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JButton</span>(<span class="string">"Button 1"</span>); <span class="comment">// 创建一个 JButton</span></span><br><span class="line">        <span class="type">JButton</span> <span class="variable">button2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JButton</span>(<span class="string">"Button 2"</span>); <span class="comment">// 创建另一个 JButton</span></span><br><span class="line"></span><br><span class="line">        add(button1); <span class="comment">// 向 JPanel 中添加 JButton</span></span><br><span class="line">        add(button2); <span class="comment">// 向 JPanel 中添加另一个 JButton</span></span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">        <span class="type">JFrame</span> <span class="variable">frame</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JFrame</span>(<span class="string">"My Frame"</span>); <span class="comment">// 创建一个 JFrame</span></span><br><span class="line"></span><br><span class="line">        <span class="type">MyPanel</span> <span class="variable">panel</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MyPanel</span>(); <span class="comment">// 创建一个 MyPanel</span></span><br><span class="line">        frame.add(panel); <span class="comment">// 将 MyPanel 添加到 JFrame 中</span></span><br><span class="line"></span><br><span class="line">        frame.setSize(<span class="number">400</span>, <span class="number">300</span>); <span class="comment">// 设置 JFrame 的大小</span></span><br><span class="line">        frame.setLocationRelativeTo(<span class="literal">null</span>); <span class="comment">// 将 JFrame 居中显示</span></span><br><span class="line">        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); <span class="comment">// 设置 JFrame 关闭时的操作为退出程序</span></span><br><span class="line">        frame.setVisible(<span class="literal">true</span>); <span class="comment">// 显示 JFrame</span></span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<h2 id="JTabbedPane"><a href="#JTabbedPane" class="headerlink" title="JTabbedPane"></a>JTabbedPane</h2><p><code>JTabbedPane</code>是一个选项卡组件，用于在同一个界面中切换不同的选项卡，每个选项卡可以包含不同的 GUI 组件。JTabbedPane通常用于显示不同的视图或功能模块，使用户可以快速地切换不同的页面。</p>
<p><code>JTabbedPane</code>还有个有参构造器<code>public JTabbedPane(int tabPlacement)</code>，可以在创建<code>JTabbedPane</code>时设置选项卡的位置，位置参数包括<code>JTabbedPane.TOP</code>、<code>JTabbedPane.BOTTOM</code>、<code>JTabbedPane.LEFT</code>、<code>JTabbedPane.RIGHT</code>。</p>
<p>JTabbedPane 的用法非常简单，只需要创建一个<code>JTabbedPane</code>对象，然后向其中添加多个选项卡，每个选项卡可以包含一个<code>JPanel</code>或其他GUI组件。看下面的例子：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> javax.swing.*;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyTabs</span> <span class="keyword">extends</span> <span class="title class_">JFrame</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">MyTabs</span><span class="params">()</span> {</span><br><span class="line">        <span class="type">JTabbedPane</span> <span class="variable">tabbedPane</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JTabbedPane</span>(); <span class="comment">// 创建一个 JTabbedPane</span></span><br><span class="line"></span><br><span class="line">        <span class="type">JPanel</span> <span class="variable">panel1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JPanel</span>(); <span class="comment">// 创建一个 JPanel</span></span><br><span class="line">        panel1.add(<span class="keyword">new</span> <span class="title class_">JLabel</span>(<span class="string">"This is Panel 1."</span>)); <span class="comment">// 向 JPanel 中添加 JLabel</span></span><br><span class="line">        tabbedPane.addTab(<span class="string">"Tab 1"</span>, panel1); <span class="comment">// 向 JTabbedPane 中添加选项卡</span></span><br><span class="line"></span><br><span class="line">        <span class="type">JPanel</span> <span class="variable">panel2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JPanel</span>(); <span class="comment">// 创建另一个 JPanel</span></span><br><span class="line">        panel2.add(<span class="keyword">new</span> <span class="title class_">JLabel</span>(<span class="string">"This is Panel 2."</span>)); <span class="comment">// 向 JPanel 中添加 JLabel</span></span><br><span class="line">        tabbedPane.addTab(<span class="string">"Tab 2"</span>,panel2); <span class="comment">// 向 JTabbedPane 中添加另一个选项卡</span></span><br><span class="line"></span><br><span class="line">        add(tabbedPane); <span class="comment">// 将 JTabbedPane 添加到 JFrame 中</span></span><br><span class="line"></span><br><span class="line">        setTitle(<span class="string">"My Tabs"</span>); <span class="comment">// 设置 JFrame 的标题</span></span><br><span class="line">        setSize(<span class="number">400</span>, <span class="number">300</span>); <span class="comment">// 设置 JFrame 的大小</span></span><br><span class="line">        setLocationRelativeTo(<span class="literal">null</span>); <span class="comment">// 将 JFrame 居中显示</span></span><br><span class="line">        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); <span class="comment">// 设置 JFrame 关闭时的操作为退出程序</span></span><br><span class="line">        setVisible(<span class="literal">true</span>); <span class="comment">// 显示 JFrame</span></span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">MyTabs</span>(); <span class="comment">// 创建并显示一个 MyTabs</span></span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<h2 id="JScrollPane（滚动窗格）"><a href="#JScrollPane（滚动窗格）" class="headerlink" title="JScrollPane（滚动窗格）"></a>JScrollPane（滚动窗格）</h2><p>滚动窗格只可以添加一个组件，可以把一个组件放到一个滚动窗格中，然后通过滚动条来观看该组件。<code>JTextArea</code>不自带滚动条，因此需要把文本区放到一个滚动窗格中。例如：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">JScrollPane</span> <span class="variable">scroll</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JScrollPane</span>(<span class="keyword">new</span> <span class="title class_">JTextArea</span>());</span><br></pre></td></tr></tbody></table></figure>
<h2 id="JSplitPane（拆分窗格）"><a href="#JSplitPane（拆分窗格）" class="headerlink" title="JSplitPane（拆分窗格）"></a>JSplitPane（拆分窗格）</h2><p><code>JSplitPane</code>用于创建可拖动分割面板，允许用户通过拖动分割条来调整两个子组件的大小。它将容器分为两个区域，可以水平或垂直地放置两个子组件。</p>
<p>JSplitPane的构造函数如下：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">JSplitPane(<span class="type">int</span> orientation, Component leftComponent, Component rightComponent)</span><br></pre></td></tr></tbody></table></figure>
<ul>
<li><code>orientation</code>参数指定分割条的方向，可以是<code>JSplitPane.HORIZONTAL_SPLIT</code>（水平分割）或<code>JSplitPane.VERTICAL_SPLIT</code>（垂直分割）。</li>
<li><code>leftComponent</code>和<code>rightComponent</code>参数是要放置在分割条两侧的组件。</li>
</ul>
<p>JSplitPane提供了一些方法来控制和管理分割面板的行为和外观，例如：</p>
<ul>
<li><code>setDividerLocation(double proportionalLocation)</code>：设置分割条的位置，使用比例值（0.0到1.0之间的double）表示位置相对于容器大小的比例。</li>
<li><code>setDividerSize(int newSize)</code>：设置分割条的大小（以像素为单位）。</li>
<li><code>setResizeWeight(double value)</code>：设置两个子组件的调整大小权重，指定它们在分割面板上分配可用空间的比例。</li>
</ul>
<h1 id="使用继承JFrame结构"><a href="#使用继承JFrame结构" class="headerlink" title="使用继承JFrame结构"></a>使用继承JFrame结构</h1><p>在Java GUI编程中，通常让一个类继承<code>JFrame</code>类，并用该类来构建窗口，因为<code>JFrame</code>类提供了一个基本的顶层容器，用于创建和管理GUI应用程序的窗口。</p>
<p>继承<code>JFrame</code>类的好处是：</p>
<ol>
<li>封装窗口逻辑：通过继承<code>JFrame</code>类，可以将窗口相关的逻辑封装在一个类中。这样，你可以将窗口的创建、初始化、布局和事件处理等操作集中在一个类中，使代码更加有组织性和可维护性。</li>
<li>扩展窗口功能：通过继承<code>JFrame</code>类，你可以自定义和扩展窗口的功能。你可以添加自定义的组件、定义窗口的样式和外观，以及实现窗口的自定义行为。</li>
</ol>
<p>下面是一个简单的示例，展示了一个继承<code>JFrame</code>类的自定义窗口类：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> javax.swing.JFrame;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyWindow</span> <span class="keyword">extends</span> <span class="title class_">JFrame</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">MyWindow</span><span class="params">()</span> {</span><br><span class="line">        <span class="comment">// 设置窗口的标题</span></span><br><span class="line">        setTitle(<span class="string">"My Window"</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 设置窗口的大小</span></span><br><span class="line">        setSize(<span class="number">400</span>, <span class="number">300</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 设置窗口关闭时的操作</span></span><br><span class="line">        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 添加其他组件和布局等操作...</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 显示窗口</span></span><br><span class="line">        setVisible(<span class="literal">true</span>);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">        <span class="comment">// 创建自定义窗口对象</span></span><br><span class="line">        <span class="type">MyWindow</span> <span class="variable">window</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MyWindow</span>();</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<h1 id="布局管理器"><a href="#布局管理器" class="headerlink" title="布局管理器"></a>布局管理器</h1><h2 id="布局管理器介绍"><a href="#布局管理器介绍" class="headerlink" title="布局管理器介绍"></a>布局管理器介绍</h2><p>通常，组件放在容器里，<strong>布局管理器</strong>决定容器中组件的位置和大小。</p>
<p>每个容器都有一个默认的管理器，但可以使用以下语句重新设置：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">panel.setLayout(<span class="keyword">new</span> <span class="title class_">GirldLayout</span>(<span class="number">4</span>,<span class="number">4</span>)); <span class="comment">//其中panel是一个具体的容器</span></span><br></pre></td></tr></tbody></table></figure>
<p>上面的语句会使用<code>GirdLayout</code>类按4行4列摆放组件。往容器中添加组件时，容器的<code>add()</code>方法会把组件和所有位置要求传递给布局管理器。</p>
<p>使用布局管理器的好处是可以实现可伸缩、适应不同屏幕大小和方便的组件排列。以下是几个常用的布局管理器：</p>
<ol>
<li>BorderLayout（边界布局管理器）：<ul>
<li>将容器划分为5个区域：北、南、东、西和中。</li>
<li>默认情况下，组件放置在中心区域，其他区域可以放置边框组件。</li>
<li>每个区域只能放一个组件，如果某个区域已经有组件了还向其中插入组件，则会替换先前的组件。</li>
<li>适用于需要将组件放置在容器的边缘的布局。</li>
</ul>
</li>
<li>FlowLayout（流式布局管理器）：<ul>
<li>以左到右、上到下的顺序依次排列组件。</li>
<li>组件的大小默认为最佳大小，也即刚好能让组件中的内容完整显示。</li>
<li>当组件超过容器的宽度时，会自动换行。</li>
<li>添加到<code>FlowLayout</code>布局的容器中的组件无法调整大小，需要调用<code>setPreferredSize(Dimension preferredSize)</code>设置大小，例如：<code>button.setPreferredSize(new Dimension(20,20))</code>。</li>
<li><code>FlowLayout</code>布局对象调用<code>setLayoutAlignment(int alignment)</code>方法可以设置组件在容器中的对齐方式。alignment参数可以是以下常量之一：<code>FlowLayout.LEFT</code>（默认，左对齐），<code>FlowLayout.CENTER</code>（居中对齐），<code>FlowLayout.RIGHT</code>（右对齐）。</li>
<li>适用于需要按顺序排列组件的布局。</li>
</ul>
</li>
<li>GridLayout（网格布局管理器）：<ul>
<li>将容器划分为网格状的行和列，每个单元格可以放置一个组件。</li>
<li>组件按照指定的行数和列数进行均匀分布。</li>
<li>适用于需要将组件等分放置的布局。</li>
</ul>
</li>
<li>CardLayout（卡片布局管理器）：<ul>
<li>允许在同一个容器中堆叠多个组件，每次只显示其中一个组件。</li>
<li>可以通过切换显示的组件来实现切换界面的效果。</li>
<li>适用于需要实现多个界面切换的布局。</li>
</ul>
</li>
<li>GridBagLayout（网格包布局管理器）：<ul>
<li>具有灵活的布局方式，可以按照组件的需求设置行和列的大小。</li>
<li>支持将组件放置在网格状的单元格中，且每个单元格可以占据多个行和列。</li>
<li>适用于需要精确控制组件位置和大小的布局。</li>
</ul>
</li>
</ol>
<h2 id="如何使用布局管理器"><a href="#如何使用布局管理器" class="headerlink" title="如何使用布局管理器"></a>如何使用布局管理器</h2><p>在使用布局管理器时，通常通过以下步骤实现布局：</p>
<ol>
<li>创建容器对象，如JPanel或JFrame。</li>
<li>创建并添加需要布局的组件到容器中。</li>
<li>创建合适的布局管理器对象，并将其设置给容器。</li>
<li>根据需要，设置布局管理器的属性和约束条件。</li>
<li>最后，将容器添加到父容器中或显示窗口。</li>
</ol>
<h2 id="BorderLayout（边界布局管理器）使用举例"><a href="#BorderLayout（边界布局管理器）使用举例" class="headerlink" title="BorderLayout（边界布局管理器）使用举例"></a>BorderLayout（边界布局管理器）使用举例</h2><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> javax.swing.*;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BorderLayoutExample</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">        <span class="type">JFrame</span> <span class="variable">frame</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JFrame</span>(<span class="string">"BorderLayout Example"</span>);</span><br><span class="line">        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);</span><br><span class="line"></span><br><span class="line">        <span class="type">JPanel</span> <span class="variable">panel</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JPanel</span>(<span class="keyword">new</span> <span class="title class_">BorderLayout</span>());</span><br><span class="line"></span><br><span class="line">        <span class="type">JButton</span> <span class="variable">btnNorth</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JButton</span>(<span class="string">"North"</span>);</span><br><span class="line">        panel.add(btnNorth, BorderLayout.NORTH);</span><br><span class="line"></span><br><span class="line">        <span class="type">JButton</span> <span class="variable">btnSouth</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JButton</span>(<span class="string">"South"</span>);</span><br><span class="line">        panel.add(btnSouth, BorderLayout.SOUTH);</span><br><span class="line"></span><br><span class="line">        <span class="type">JButton</span> <span class="variable">btnEast</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JButton</span>(<span class="string">"East"</span>);</span><br><span class="line">        panel.add(btnEast, BorderLayout.EAST);</span><br><span class="line"></span><br><span class="line">        <span class="type">JButton</span> <span class="variable">btnWest</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JButton</span>(<span class="string">"West"</span>);</span><br><span class="line">        panel.add(btnWest, BorderLayout.WEST);</span><br><span class="line"></span><br><span class="line">        <span class="type">JButton</span> <span class="variable">btnCenter</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JButton</span>(<span class="string">"Center"</span>);</span><br><span class="line">        panel.add(btnCenter, BorderLayout.CENTER);</span><br><span class="line"></span><br><span class="line">        frame.add(panel);</span><br><span class="line">        frame.pack();</span><br><span class="line">        frame.setVisible(<span class="literal">true</span>);</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<h2 id="FlowLayout（流式布局管理器）使用举例"><a href="#FlowLayout（流式布局管理器）使用举例" class="headerlink" title="FlowLayout（流式布局管理器）使用举例"></a>FlowLayout（流式布局管理器）使用举例</h2><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> javax.swing.*;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">FlowLayoutExample</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">        <span class="type">JFrame</span> <span class="variable">frame</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JFrame</span>(<span class="string">"FlowLayout Example"</span>);</span><br><span class="line">        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);</span><br><span class="line"></span><br><span class="line">        <span class="type">JPanel</span> <span class="variable">panel</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JPanel</span>(<span class="keyword">new</span> <span class="title class_">FlowLayout</span>());</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt;= <span class="number">5</span>; i++) {</span><br><span class="line">            <span class="type">JButton</span> <span class="variable">button</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JButton</span>(<span class="string">"Button "</span> + i);</span><br><span class="line">            panel.add(button);</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        frame.add(panel);</span><br><span class="line">        frame.pack();</span><br><span class="line">        frame.setVisible(<span class="literal">true</span>);</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<h2 id="GridLayout（网格布局管理器）使用举例"><a href="#GridLayout（网格布局管理器）使用举例" class="headerlink" title="GridLayout（网格布局管理器）使用举例"></a>GridLayout（网格布局管理器）使用举例</h2><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> javax.swing.*;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">GridLayoutExample</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">        <span class="type">JFrame</span> <span class="variable">frame</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JFrame</span>(<span class="string">"GridLayout Example"</span>);</span><br><span class="line">        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);</span><br><span class="line"></span><br><span class="line">        <span class="type">JPanel</span> <span class="variable">panel</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JPanel</span>(<span class="keyword">new</span> <span class="title class_">GridLayout</span>(<span class="number">3</span>, <span class="number">3</span>));</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt;= <span class="number">9</span>; i++) {</span><br><span class="line">            <span class="type">JButton</span> <span class="variable">button</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JButton</span>(<span class="string">"Button "</span> + i);</span><br><span class="line">            panel.add(button);</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        frame.add(panel);</span><br><span class="line">        frame.pack();</span><br><span class="line">        frame.setVisible(<span class="literal">true</span>);</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<h2 id="CardLayout（卡片布局管理器）使用举例"><a href="#CardLayout（卡片布局管理器）使用举例" class="headerlink" title="CardLayout（卡片布局管理器）使用举例"></a>CardLayout（卡片布局管理器）使用举例</h2><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> javax.swing.*;</span><br><span class="line"><span class="keyword">import</span> java.awt.*;</span><br><span class="line"><span class="keyword">import</span> java.awt.event.ActionEvent;</span><br><span class="line"><span class="keyword">import</span> java.awt.event.ActionListener;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CardLayoutExample</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">        <span class="type">JFrame</span> <span class="variable">frame</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JFrame</span>(<span class="string">"CardLayout Example"</span>);</span><br><span class="line">        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);</span><br><span class="line"></span><br><span class="line">        <span class="type">JPanel</span> <span class="variable">panel</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JPanel</span>(<span class="keyword">new</span> <span class="title class_">CardLayout</span>());</span><br><span class="line"></span><br><span class="line">        <span class="type">JButton</span> <span class="variable">btn1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JButton</span>(<span class="string">"Card 1"</span>);</span><br><span class="line">        panel.add(btn1, <span class="string">"Card 1"</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">JButton</span> <span class="variable">btn2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JButton</span>(<span class="string">"Card 2"</span>);</span><br><span class="line">        panel.add(btn2, <span class="string">"Card 2"</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">JButton</span> <span class="variable">btn3</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JButton</span>(<span class="string">"Card 3"</span>);</span><br><span class="line">        panel.add(btn3, <span class="string">"Card 3"</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">JButton</span> <span class="variable">btnNext</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JButton</span>(<span class="string">"Next"</span>);</span><br><span class="line">        btnNext.addActionListener(<span class="keyword">new</span> <span class="title class_">ActionListener</span>() {</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">actionPerformed</span><span class="params">(ActionEvent e)</span> {</span><br><span class="line">                <span class="type">CardLayout</span> <span class="variable">cardLayout</span> <span class="operator">=</span> (CardLayout) panel.getLayout();</span><br><span class="line">                cardLayout.next(panel);</span><br><span class="line">            }</span><br><span class="line">        });</span><br><span class="line">        frame.add(btnNext, BorderLayout.SOUTH);</span><br><span class="line">        frame.add(panel, BorderLayout.CENTER);</span><br><span class="line"></span><br><span class="line">        frame.pack();</span><br><span class="line">        frame.setVisible(<span class="literal">true</span>);</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<h1 id="GUI中常用的类"><a href="#GUI中常用的类" class="headerlink" title="GUI中常用的类"></a>GUI中常用的类</h1><h2 id="Color类"><a href="#Color类" class="headerlink" title="Color类"></a>Color类</h2><p><code>Color</code>类是用于表示颜色的类。它包含了表示各种颜色的预定义常量，同时也可以通过RGB或HSB（色调、饱和度、亮度）值来创建自定义颜色。</p>
<p>以下是<code>Color</code>类的一些常用方法和用法：</p>
<h3 id="使用预定义颜色常量"><a href="#使用预定义颜色常量" class="headerlink" title="使用预定义颜色常量"></a>使用预定义颜色常量</h3><p><code>Color</code>类提供了一些预定义的常量，如<code>RED</code>、<code>GREEN</code>、<code>BLUE</code>等，用于表示基本颜色。看下面的例子：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Color</span> <span class="variable">red</span> <span class="operator">=</span> Color.RED;</span><br><span class="line"><span class="type">Color</span> <span class="variable">green</span> <span class="operator">=</span> Color.GREEN;</span><br><span class="line"><span class="type">Color</span> <span class="variable">blue</span> <span class="operator">=</span> Color.BLUE;</span><br></pre></td></tr></tbody></table></figure>
<h3 id="创建自定义颜色"><a href="#创建自定义颜色" class="headerlink" title="创建自定义颜色"></a>创建自定义颜色</h3><p>可以使用<code>new Color(int r, int g, int b)</code>来创建自定义的RGB颜色，其中<code>r</code>、<code>g</code>和<code>b</code>是0到255之间的整数。看下面的例子：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Color</span> <span class="variable">customColor</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Color</span>(<span class="number">255</span>, <span class="number">128</span>, <span class="number">0</span>); <span class="comment">// 使用RGB值创建自定义颜色</span></span><br></pre></td></tr></tbody></table></figure>
<h2 id="Font类"><a href="#Font类" class="headerlink" title="Font类"></a>Font类</h2><p><code>Font</code>类用于表示字体，它提供了控制文本显示样式和特征的功能。可以使用<code>Font</code>类来设置文本的字体、样式、大小和其他属性。</p>
<p>以下是<code>Font</code>类的一些常用方法和用法：</p>
<p>首先，创建一个<code>Font</code>对象.使用<code>Font</code>类的构造函数，您可以创建一个字体对象。参数包括字体名称、样式（粗体、斜体、常规）和字体大小:</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Font</span> <span class="variable">font</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Font</span>(String name, <span class="type">int</span> style, <span class="type">int</span> size);</span><br></pre></td></tr></tbody></table></figure>
<p>其中，<code>style</code>参数包括下列取值：</p>
<ul>
<li><code>Font.PLAIN</code>：常规样式，无粗体或斜体效果。</li>
<li><code>Font.BOLD</code>：粗体样式。</li>
<li><code>Font.ITALIC</code>：斜体样式。</li>
<li><code>Font.BOLD + Font.ITALIC</code>：粗体和斜体样式的组合。</li>
</ul>
<p>在组件中使用字体可以通过：<code>组件.setFont(Font font)</code>来改变组件的默认字体。</p>
<h2 id="ImageIcon类"><a href="#ImageIcon类" class="headerlink" title="ImageIcon类"></a>ImageIcon类</h2><p><code>ImageIcon</code>类是Java Swing中用于表示图像图标的类。它是<code>Icon</code>接口的实现类，可用于在GUI应用程序中显示图像。</p>
<p>以下是<code>ImageIcon</code>类的一些重要方法和用法：</p>
<h3 id="创建ImageIcon对象"><a href="#创建ImageIcon对象" class="headerlink" title="创建ImageIcon对象"></a>创建<code>ImageIcon</code>对象</h3><p>可以使用不同的构造函数创建<code>ImageIcon</code>对象。可以提供图像文件的文件名、图像的URL地址或现有的<code>Image</code>对象：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">ImageIcon</span> <span class="variable">icon</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ImageIcon</span>(String filename);</span><br><span class="line"><span class="type">ImageIcon</span> <span class="variable">icon</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ImageIcon</span>(URL url);</span><br><span class="line"><span class="type">ImageIcon</span> <span class="variable">icon</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ImageIcon</span>(Image image);</span><br></pre></td></tr></tbody></table></figure>
<h3 id="获取图像的宽度和高度"><a href="#获取图像的宽度和高度" class="headerlink" title="获取图像的宽度和高度"></a>获取图像的宽度和高度</h3><p>使用<code>getIconWidth()</code>和<code>getIconHeight()</code>方法，可以获取图像图标的宽度和高度:</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="variable">width</span> <span class="operator">=</span> icon.getIconWidth();</span><br><span class="line"><span class="type">int</span> <span class="variable">height</span> <span class="operator">=</span> icon.getIconHeight();</span><br></pre></td></tr></tbody></table></figure>
<h3 id="设置图标的描述文本"><a href="#设置图标的描述文本" class="headerlink" title="设置图标的描述文本"></a>设置图标的描述文本</h3><p>使用<code>setDescription()</code>方法，可以为图标设置描述文本。这对于在鼠标悬停时显示工具提示信息很有用:</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">icon.setDescription(String description);</span><br></pre></td></tr></tbody></table></figure>
<h3 id="获取图标的图像"><a href="#获取图标的图像" class="headerlink" title="获取图标的图像"></a>获取图标的图像</h3><p> 使用<code>getImage()</code>方法，可以获取<code>ImageIcon</code>对象中包含的图像:</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Image</span> <span class="variable">image</span> <span class="operator">=</span> icon.getImage();</span><br></pre></td></tr></tbody></table></figure>
<h3 id="IconImage类在GUI编程中的应用"><a href="#IconImage类在GUI编程中的应用" class="headerlink" title="IconImage类在GUI编程中的应用"></a><code>IconImage</code>类在GUI编程中的应用</h3><p>在GUI编程中，可以将<code>ImageIcon</code>应用于许多组件和场景，例如：</p>
<ul>
<li><p>将图标显示在<code>JLabel</code>中：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">JLabel</span> <span class="variable">label</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JLabel</span>();</span><br><span class="line">label.setIcon(icon);</span><br></pre></td></tr></tbody></table></figure>
</li>
<li><p>在按钮上显示图标：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">JButton</span> <span class="variable">button</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JButton</span>();</span><br><span class="line">button.setIcon(icon);</span><br></pre></td></tr></tbody></table></figure>
</li>
<li><p>在菜单项上显示图标：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">JMenuItem</span> <span class="variable">menuItem</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JMenuItem</span>();</span><br><span class="line">menuItem.setIcon(icon);</span><br></pre></td></tr></tbody></table></figure>
</li>
<li><p>在对话框中显示图标：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">JOptionPane.showMessageDialog(<span class="literal">null</span>, <span class="string">"Message"</span>, <span class="string">"Title"</span>, JOptionPane.INFORMATION_MESSAGE, icon);</span><br></pre></td></tr></tbody></table></figure>
</li>
</ul>
<p>通过将<code>ImageIcon</code>对象与适当的Swing组件结合使用，可以在GUI应用程序中显示图像图标，并为用户提供更丰富的视觉体验。</p>
<h1 id="事件处理"><a href="#事件处理" class="headerlink" title="事件处理"></a>事件处理</h1><p>在Java中，事件处理是指对用户交互或系统事件作出响应的机制。它允许程序捕获和处理事件，例如按钮点击、鼠标移动、键盘按下等。</p>
<p>Java事件处理基于事件和监听器。以下是事件处理的基本概念：</p>
<ol>
<li>事件（Event）：<strong>事件是发生在应用程序中的特定操作或状态变化</strong>。例如，按钮被点击、鼠标移动、键盘按下等都是事件。每个事件都与特定的类关联，例如<code>ActionEvent</code>、<code>MouseEven</code>t、<code>KeyEvent</code>等。</li>
<li>事件源（Event Source）：<strong>事件源是事件发生的对象或组件</strong>。它是可以触发事件的实体，例如按钮、文本框、鼠标等。事件源类通常是派生自<code>AWT</code>或<code>Swing</code>类库中的组件类。</li>
<li>监听器（Listener）：<strong>监听器是负责接收和处理事件的对象</strong>。它实现了事件监听器接口，该接口定义了处理事件的方法。<strong>监听器会注册到事件源上，以便在事件发生时被通知</strong>。<strong>每种类型的事件都有对应的监听器接口</strong>，例如<code>ActionListener</code>、<code>MouseListener</code>、<code>KeyListener</code>等。</li>
<li>注册监听器（Register Listener）：在Java中，<strong>需要将监听器注册到事件源上</strong>，以便在事件发生时能够接收到通知。通常使用事件源的<code>addXXXListener()</code>方法进行注册，其中<code>XXX</code>是特定事件类型的名称。</li>
<li>事件处理方法（Event Handling Method）：事件处理方法是监听器接口中定义的方法，用于处理特定类型的事件。当事件发生时，事件源会调用监听器的事件处理方法，将事件对象作为参数传递给方法，以便处理事件。</li>
</ol>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://xiaoqingming18.github.io/2023/05/18/%E6%95%B0%E6%8D%AE%E5%BA%93%E5%8E%9F%E7%90%86%EF%BC%9A%E6%95%B0%E6%8D%AE%E5%BA%93%E8%AE%BE%E8%AE%A1/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="鸣蜩十七">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="鸣蜩十七">
      <meta itemprop="description" content="记录我的学习、生活和恋爱日常">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 鸣蜩十七">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2023/05/18/%E6%95%B0%E6%8D%AE%E5%BA%93%E5%8E%9F%E7%90%86%EF%BC%9A%E6%95%B0%E6%8D%AE%E5%BA%93%E8%AE%BE%E8%AE%A1/" class="post-title-link" itemprop="url">数据库原理：数据库设计</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2023-05-18 19:38:03" itemprop="dateCreated datePublished" datetime="2023-05-18T19:38:03+08:00">2023-05-18</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-05-29 22:44:16" itemprop="dateModified" datetime="2023-05-29T22:44:16+08:00">2023-05-29</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E6%95%B0%E6%8D%AE%E5%BA%93%E5%8E%9F%E7%90%86/" itemprop="url" rel="index"><span itemprop="name">-数据库原理</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="数据库设计流程"><a href="#数据库设计流程" class="headerlink" title="数据库设计流程"></a>数据库设计流程</h1><h2 id="数据库设计概述"><a href="#数据库设计概述" class="headerlink" title="数据库设计概述"></a>数据库设计概述</h2><p><strong>数据库设计</strong>是指对于一个给定的应用环境，构造最优的数据库模式，建立数据库及其应用系统，使之能够有效地存储数据，满足各种用户的应用需求。主要包括 2 种需求：</p>
<div class="table-container">
<table>
<thead>
<tr>
<th>需求类型</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>信息管理要求</td>
<td>指在数据库中应该存储和管理哪些数据对象</td>
</tr>
<tr>
<td>数据操作要求</td>
<td>指对数据对象需要进行哪些操作，如查询、增、删、改、统计等操作</td>
</tr>
</tbody>
</table>
</div>
<p>数据库设计包括数据库的结构设计和行为设计两方面的内容，数据库模式是静态的、稳定的，所以<strong>结构设计</strong>又称为静态模型设计，行为设计是动态的，所以<strong>行为设计</strong>又称为动态模型设计。</p>
<div class="table-container">
<table>
<thead>
<tr>
<th>设计内容</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>结构设计</td>
<td>根据给定的应用环境，进行数据库的模式或子模式的设计，包括数据库的概念设计、逻辑设计和物理设计</td>
</tr>
<tr>
<td>行为设计</td>
<td>指确定数据库用户的行为和动作，也就是对数据库的操作，而这些要通过应用程序来实现</td>
</tr>
</tbody>
</table>
</div>
<h2 id="数据库设计步骤"><a href="#数据库设计步骤" class="headerlink" title="数据库设计步骤"></a>数据库设计步骤</h2><p>数据库设计一般分为六个阶段：</p>
<ol>
<li>需求分析：准确了解与分析用户需求，是后续工作的重要基础；</li>
<li>概念结构设计：对用户需求进行综合、归纳、抽象，形成概念模型；</li>
<li>逻辑结构设计：将概念结构转换为 DBMS 支持的数据模型；</li>
<li>物理结构设计：为逻辑数据模型选择适用的物理结构；</li>
<li>数据库实施：基于数据库进行程序设计，并且调试运行；</li>
<li>数据库运行和维护：正式将项目落地，运行过程中不断对数据库进行评估、调整和修改。</li>
</ol>
<p>下图概括地给出了设计过程各个阶段关于数据特性的设计描述：</p>
<p><img src="/2023/05/18/%E6%95%B0%E6%8D%AE%E5%BA%93%E5%8E%9F%E7%90%86%EF%BC%9A%E6%95%B0%E6%8D%AE%E5%BA%93%E8%AE%BE%E8%AE%A1/数据库设计各个阶段的数据设计描述.png" alt="数据库设计各个阶段的数据设计描述"></p>
<h1 id="需求分析"><a href="#需求分析" class="headerlink" title="需求分析"></a>需求分析</h1><p>需求分析简单地说就是分析用户的要求，是通过详细调查现实世界要处理的对象，充分了解原系统工作概况，明确用户的各种需求，是设计数据库的起点。需求分析结果是否准确直接影响设计结果是否合理和实用。</p>
<h2 id="需求调查"><a href="#需求调查" class="headerlink" title="需求调查"></a>需求调查</h2><p>通过调查、收集与分析，获得用户对数据库的如下要求：</p>
<div class="table-container">
<table>
<thead>
<tr>
<th>对数据库的要求</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>信息要求</td>
<td>用户需要从数据库中获得信息的内容与性质，在数据库中需要存储哪些数据</td>
</tr>
<tr>
<td>处理要求</td>
<td>用户要完成的数据处理功能，对处理性能的要求</td>
</tr>
<tr>
<td>安全性要求</td>
<td>体现在数据库安全性、信息安全性和系统平台的安全性等方面</td>
</tr>
<tr>
<td>完整性要求</td>
<td>系统完整性要求系统中数据的正确性以及相容性</td>
</tr>
</tbody>
</table>
</div>
<h2 id="数据流图"><a href="#数据流图" class="headerlink" title="数据流图"></a>数据流图</h2><h2 id="数据字典"><a href="#数据字典" class="headerlink" title="数据字典"></a>数据字典</h2><h1 id="概念结构设计"><a href="#概念结构设计" class="headerlink" title="概念结构设计"></a>概念结构设计</h1><p>将需求分析得到的用户需求抽象为信息世界的结构，<strong>概念结构设计</strong>的任务是将需求分析的结果进行概念化抽象，获得系统的 E-R 图。</p>
<h2 id="E-R模型"><a href="#E-R模型" class="headerlink" title="E-R模型"></a>E-R模型</h2><p>E-R模型是用E-R图来描述现实世界的概念模型，主要涉及以下一些概念：</p>
<div class="table-container">
<table>
<thead>
<tr>
<th>概念</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>实体</td>
<td>客观存在并可相互区别的事物称为实体</td>
</tr>
<tr>
<td>属性</td>
<td>实体所具有的某一特性称为属性</td>
</tr>
<tr>
<td>码</td>
<td>唯一标识实体的属性集称为码</td>
</tr>
<tr>
<td>实体型</td>
<td>就是用实体名及其属性名集合来抽象和刻画同类实体</td>
</tr>
<tr>
<td>实体集</td>
<td>同一类型实体的集合称为实体集</td>
</tr>
<tr>
<td>联系</td>
<td>不同实体集之间的联系，主要有一对一、一对多和多对多三种联系</td>
</tr>
</tbody>
</table>
</div>
<h2 id="实体之间的联系与E-R图"><a href="#实体之间的联系与E-R图" class="headerlink" title="实体之间的联系与E-R图"></a>实体之间的联系与E-R图</h2><h3 id="实体之间的联系"><a href="#实体之间的联系" class="headerlink" title="实体之间的联系"></a>实体之间的联系</h3><p>在E-R模型中，实体之间的联系用于描述实体之间的关系和联系。实体之间的联系可以分为三种类型：一对一关系、一对多关系和多对多关系。</p>
<h3 id="两个实体之间的联系"><a href="#两个实体之间的联系" class="headerlink" title="两个实体之间的联系"></a>两个实体之间的联系</h3><p><strong>一对一关系</strong>：</p>
<p>一对一关系是指两个实体之间只存在一种对应关系，即一个实体对应另一个实体，而且每个实体只能对应一个实体。在E-R模型中，一对一关系可以用一条直线连接两个实体表示。例如，一个人只有一个身份证号码，一个身份证号码只对应一个人，因此人和身份证之间是一对一关系。</p>
<p><strong>一对多关系</strong>：</p>
<p>一对多关系是指一个实体可以对应多个实体，而一个实体只能对应一个实体。在E-R模型中，一对多关系可以用一个箭头从多的一端指向一的一端表示。例如，一个部门可以有多个员工，而每个员工只能属于一个部门，因此部门和员工之间是一对多关系。</p>
<p><strong>多对多关系</strong>：</p>
<p>多对多关系是指两个实体之间存在多个对应关系，即一个实体可以对应多个实体，而且一个实体也可以被多个实体对应。在E-R模型中，多对多关系可以用一个双向箭头连接两个实体表示。例如，一个学生可以选择多门课程，一门课程也可以有多个学生选修，因此学生和课程之间是多对多关系。</p>
<p>下面是各种联系用E-R图表示的方法：</p>
<p><img src="/2023/05/18/%E6%95%B0%E6%8D%AE%E5%BA%93%E5%8E%9F%E7%90%86%EF%BC%9A%E6%95%B0%E6%8D%AE%E5%BA%93%E8%AE%BE%E8%AE%A1/联系图表示法.png" alt="各种联系用E-R图表示的方法"></p>
<h3 id="两个以上实体的联系"><a href="#两个以上实体的联系" class="headerlink" title="两个以上实体的联系"></a>两个以上实体的联系</h3><p>一般在两个以上的实体之间也存在一对一、一对多和多对多的联系。</p>
<p>例如，对于课程、教师和参考书三个实体型，一门课可以有多个教师和多本教科书，但每个教室只教授一门课，一本参考书只用于一门课程，这便是<strong>三个实体间的一对多联系</strong>。</p>
<p>又如，对于供应商、项目和零件三个实体型，一个供应商可以供应多个项目和多个零件，而一个项目可以由多个供应商供应的零件，每个零件可由不同的供应商供应，这便是<strong>三个实体间的多对多联系</strong>。</p>
<p>下面给出这两个例子的E-R图：</p>
<p><img src="/2023/05/18/%E6%95%B0%E6%8D%AE%E5%BA%93%E5%8E%9F%E7%90%86%EF%BC%9A%E6%95%B0%E6%8D%AE%E5%BA%93%E8%AE%BE%E8%AE%A1/三个实体间的联系.png" alt="三个实体间的联系"></p>
<h3 id="单个实体集间的联系"><a href="#单个实体集间的联系" class="headerlink" title="单个实体集间的联系"></a>单个实体集间的联系</h3><p>同一实体集的各个实体间也可以存在一对一、一对多和多对多联系。例如，职工实体型内部具有领导与被领导的联系，即某一职工（干部）领导若干名职工，而一名职工只被另外一个职工（干部）领导，这就是<strong>单个实体集间的一对多联系</strong>。该例子的E-R图如下：</p>
<p><img src="/2023/05/18/%E6%95%B0%E6%8D%AE%E5%BA%93%E5%8E%9F%E7%90%86%EF%BC%9A%E6%95%B0%E6%8D%AE%E5%BA%93%E8%AE%BE%E8%AE%A1/单个实体集间的一对多联系.png" alt="单个实体集间的一对多联系"></p>
<p>一般的，把参与联系的实体型的数目称为联系的<strong>度</strong>，两个实体型之间的联系称为<strong>二元联系</strong>，三个实体型之间的联系称为<strong>三元联系</strong>，N个实体型之间的联系称为<strong>N元联系</strong>。</p>
<h2 id="E-R图"><a href="#E-R图" class="headerlink" title="E-R图"></a>E-R图</h2><p> <strong>E-R 图</strong>被用来描述现实世界的概念模型。E-R 图提供了表示实体型、属性和联系的方法：</p>
<div class="table-container">
<table>
<thead>
<tr>
<th>E-R 图组件</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>实体型</td>
<td>用矩形表示，矩形框内写明实体名</td>
</tr>
<tr>
<td>属性</td>
<td>用椭圆形表示，并用无向边将其与相应的实体型连接起来</td>
</tr>
<tr>
<td>联系</td>
<td>用菱形表示，菱形框内写明联系名，用无向边分别与有关实体型连接起来，同时在无向边旁标上联系的类型</td>
</tr>
</tbody>
</table>
</div>
<h1 id="逻辑结构设计"><a href="#逻辑结构设计" class="headerlink" title="逻辑结构设计"></a>逻辑结构设计</h1><p><strong>逻辑结构设计</strong>的任务就是把概念结构设计阶段设计好的基本 E-R 图转换为与选用数据库管理系统产品所支持的数据模型相符合的逻辑结构。</p>
<h2 id="E-R图转换为关系模型"><a href="#E-R图转换为关系模型" class="headerlink" title="E-R图转换为关系模型"></a>E-R图转换为关系模型</h2><p>将 E-R 图转换为关系模型实际上就是要将实体型、属性和联系转换为关系模式，一个实体型转换为一个关系模式，牢记一个规则：<strong>关系的属性就是实体的属性，关系的码就是实体的码</strong>。对于实体型间的联系有以下不同的情况：</p>
<ol>
<li>一个 1:1 联系可以转换为一个独立的关系模式，也可以与任意一端对应的关系模式合并。</li>
<li>一个 1:n 联系可以转换为一个独立的关系模式，也可以与 n 端对应的关系模式合并。如果转换为一个独立的关系模式，则与该联系相连的各实体的码以及联系本身的属性均转换为关系的属性，而关系的码为 n 端实体的码。</li>
<li>一个 m:n 联系转换为一个关系模式，与该联系相连的各实体的码以及联系本身的属性均转换为关系的属性，而关系的码为各实体码的组合。</li>
<li>三个或三个以上实体间的一个多元联系可以转换为一个关系模式，与该多元联系相连的各实体的码以及联系本身的属性均转换为关系的属性，而关系的码为各实体码的组合。</li>
<li>具有相同码的关系模式可合并。</li>
</ol>
<p>我们用人话来解释一下两个实体间的E-R图如何转换为关系模型：</p>
<p>第一步，将各个实体的名字转换为各个关系模式的名字；</p>
<p>第二步，实体的属性就是关系额属性，实体的码就是关系的码；</p>
<p>第三步，实体间联系的转换：</p>
<ol>
<li>1对1联系：在任意一方加入另一方的主码作为这一方的外码，并将联系本身的属性也加入该方作为属性；</li>
<li>1对n联系：讲1方的主码加入n方作为n方的外码，同时将联系本身的属性加入n方；</li>
<li>n对m联系；将联系本身转换为一个关系模式，并将联系本身的属性加入该关系模式，再将双方的主码加入其中作为码。</li>
</ol>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://xiaoqingming18.github.io/2023/05/10/Java%EF%BC%9A%E5%8F%8D%E5%B0%84/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="鸣蜩十七">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="鸣蜩十七">
      <meta itemprop="description" content="记录我的学习、生活和恋爱日常">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 鸣蜩十七">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2023/05/10/Java%EF%BC%9A%E5%8F%8D%E5%B0%84/" class="post-title-link" itemprop="url">Java：反射</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2023-05-10 21:27:15" itemprop="dateCreated datePublished" datetime="2023-05-10T21:27:15+08:00">2023-05-10</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-05-11 21:51:12" itemprop="dateModified" datetime="2023-05-11T21:51:12+08:00">2023-05-11</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%90%8E%E7%AB%AF/" itemprop="url" rel="index"><span itemprop="name">-后端</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="待解决问题"><a href="#待解决问题" class="headerlink" title="待解决问题"></a>待解决问题</h1><p>1.基本类型是否可以获取对应的Class实例？</p>
<h1 id="什么是反射"><a href="#什么是反射" class="headerlink" title="什么是反射"></a>什么是反射</h1><p><strong>Java中的反射是指在运行时动态地获取类的信息以及调用它的方法和属性的能力</strong>。它允许程序在运行时检查和修改对象的内部状态，而不需要在编译时提前知道这些信息。</p>
<p>反射的主要作用包括：</p>
<ol>
<li>动态创建对象：通过反射可以在运行时动态地创建一个类的对象，而不需要在编译时就确定类型。</li>
<li>动态调用方法：通过反射可以在运行时动态地调用一个类的方法，而不需要在编译时就确定方法名和参数类型。</li>
<li>获取类的信息：通过反射可以获取一个类的属性、构造方法、接口、父类等信息。</li>
<li>改变对象的属性值：通过反射可以在运行时改变一个对象的属性值。</li>
<li>框架开发：反射是许多框架(如Spring、Hibernate等)的核心功能之一，它们利用反射来实现各种复杂的功能。</li>
</ol>
<h1 id="Class类-获取某个类的信息"><a href="#Class类-获取某个类的信息" class="headerlink" title="Class类-获取某个类的信息"></a>Class类-获取某个类的信息</h1><h2 id="什么是Class类"><a href="#什么是Class类" class="headerlink" title="什么是Class类"></a>什么是Class类</h2><p>Java的<code>Class</code>类是用来描述Java类和接口的元数据(metadata)的。它提供了一些方法来获取一个类的信息，例如类名、父类、接口、字段、方法等。</p>
<h2 id="得到Class类的一个实例"><a href="#得到Class类的一个实例" class="headerlink" title="得到Class类的一个实例"></a>得到Class类的一个实例</h2><p>获取<code>Class</code>类对象的方法一共有3种：</p>
<p>第一种，通过<code>Object</code>类的<code>getClass()</code>方法返回一个<code>Class</code>实例：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Employee e;</span><br><span class="line"><span class="type">Class</span> <span class="variable">cl</span> <span class="operator">=</span> e.getClass();</span><br></pre></td></tr></tbody></table></figure>
<p>第二种，通过<code>Class</code>类的静态方法<code>forName()</code>获得类名对应的<code>Class</code>对象：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">className</span> <span class="operator">=</span> <span class="string">"java.util.Random"</span>;</span><br><span class="line"><span class="type">Class</span> <span class="variable">cl</span> <span class="operator">=</span> Class.forName(className);</span><br></pre></td></tr></tbody></table></figure>
<p>使用<code>forName()</code>方法是要注意，若要获取的类与当前类不在同一个包里，需要将要获取的类所在的包也要作为类名的一部分，如上例中的<code>java.util.Random</code>。</p>
<p><strong>第三种是一种更简便的方式</strong>，假设<code>T</code>是一个具体的类，可以通过<code>T.class</code>得到与之匹配的<code>Class</code>类对象：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Class</span> <span class="variable">cl</span> <span class="operator">=</span> Random.class;</span><br><span class="line"><span class="type">Class</span> <span class="variable">cl2</span> <span class="operator">=</span> <span class="type">int</span>.class;</span><br></pre></td></tr></tbody></table></figure>
<p>要注意的是，<strong>一个<code>Class</code>对象实际上表示的是一个类型，可能是类，也可能不是类。</strong>例如上面例子里的<code>int</code>不是类，但<code>int.class</code>是一个<code>Class</code>类型的对象。</p>
<h2 id="Class类的常用方法"><a href="#Class类的常用方法" class="headerlink" title="Class类的常用方法"></a>Class类的常用方法</h2><p><code>Class</code>类的一些常用方法包括：</p>
<ul>
<li><code>getName()</code>:返回类的名称；</li>
<li><code>getClassLoader()</code>:返回该类的加载器；</li>
<li><code>getDeclaredFields()</code>:返回该类的所有字段，包括继承而来的字段；</li>
<li><code>getDeclaredConstructors()</code>:返回该类的所有构造方法；</li>
<li><code>getDeclaredMethods()</code>:返回该类的所有方法，包括继承而来的方法；</li>
<li><code>isInterface()</code>:判断该类是否是一个接口；</li>
<li><code>isArray()</code>:判断该类是否是数组类型；</li>
<li><code>isPrimitive()</code>:判断该类是否是基本数据类型。</li>
</ul>
<p>通过这些方法，我们可以获取到Java类的各种信息，从而进行更加灵活和高效的编程。</p>
<h2 id="Class类对象的唯一性"><a href="#Class类对象的唯一性" class="headerlink" title="Class类对象的唯一性"></a>Class类对象的唯一性</h2><p>虚拟机为每一个Java类仅提供一个对应的<code>Class</code>对象。所以，可以用比较运算符<code>==</code>实现两个<code>Class</code>对象的比较：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(e.getClass() == Employee.class);</span><br></pre></td></tr></tbody></table></figure>
<p>上面的例子中，如果<code>e</code>是<code>Employee</code>类的实例，则表达式返回<code>true</code>，如果<code>e</code>不是<code>Employee</code>的一个实例，或者<code>e</code>是<code>Employee</code>的子类对象，则表达式返回<code>false</code>。</p>
<blockquote>
<p>Class类对象的唯一性指的是每个Java类都有且只有一个对应的Class对象，即一个类在内存中只能存在一份Class对象。这是因为Java中的类是按需加载的，当程序第一次使用某个类时，才会动态加载该类的字节码文件并创建对应的Class对象，因此每个类的Class对象都是唯一的。同时，由于Java中的多态特性，同一个类的不同对象可能会有不同的Class对象，但它们都指向同一个类的字节码文件。</p>
</blockquote>
<h1 id="Field类、Method类和Constructor类"><a href="#Field类、Method类和Constructor类" class="headerlink" title="Field类、Method类和Constructor类"></a>Field类、Method类和Constructor类</h1>
      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




  <nav class="pagination">
    <a class="extend prev" rel="prev" title="上一页" aria-label="上一页" href="/"><i class="fa fa-angle-left"></i></a><a class="page-number" href="/">1</a><span class="page-number current">2</span><a class="page-number" href="/page/3/">3</a><a class="page-number" href="/page/4/">4</a><a class="extend next" rel="next" title="下一页" aria-label="下一页" href="/page/3/"><i class="fa fa-angle-right"></i></a>
  </nav>

</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">


<div class="copyright">
  &copy; 
  <span itemprop="copyrightYear">2023</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">鸣蜩十七</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.js.org/muse/" rel="noopener" target="_blank">NexT.Muse</a> 强力驱动
  </div>

    </div>
  </footer>

  
  <div class="toggle sidebar-toggle" role="button">
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
  </div>
  <div class="sidebar-dimmer"></div>
  <div class="back-to-top" role="button" aria-label="返回顶部">
    <i class="fa fa-arrow-up fa-lg"></i>
    <span>0%</span>
  </div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script>
<script src="/js/comments.js"></script><script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/schemes/muse.js"></script><script src="/js/next-boot.js"></script>

  




  




  

  <script class="next-config" data-name="enableMath" type="application/json">true</script><script class="next-config" data-name="mathjax" type="application/json">{"enable":true,"tags":"none","js":{"url":"https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.2/es5/tex-mml-chtml.js","integrity":"sha256-MASABpB4tYktI2Oitl4t+78w/lyA+D7b/s9GEP0JOGI="}}</script>
<script src="/js/third-party/math/mathjax.js"></script>



</body>
</html>
